[PATCH 1/5] Remove obsolete debugging formats from names list

2023-10-22 Thread Mark Harmstone
STABS and xcoff have been removed, but are still in debug_type_names,
which ought to match debug_type_masks. This results in the following
minor bug with GCC 13:

$ x86_64-pc-linux-gnu-gcc -gvms -c tmp.c
cc1: error: target system does not support the ‘dwarf-2’ debug format
---
 gcc/opts.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gcc/opts.cc b/gcc/opts.cc
index 573dcf8e497..8015cb7556a 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -50,7 +50,7 @@ static void set_Wstrict_aliasing (struct gcc_options *opts, 
int onoff);
 
 const char *const debug_type_names[] =
 {
-  "none", "stabs", "dwarf-2", "xcoff", "vms", "ctf", "btf"
+  "none", "dwarf-2", "vms", "ctf", "btf"
 };
 
 /* Bitmasks of fundamental debug info formats indexed by enum
@@ -65,7 +65,7 @@ static uint32_t debug_type_masks[] =
 /* Names of the set of debug formats requested by user.  Updated and accessed
via debug_set_names.  */
 
-static char df_set_names[sizeof "none stabs dwarf-2 xcoff vms ctf btf"];
+static char df_set_names[sizeof "none dwarf-2 vms ctf btf"];
 
 /* Get enum debug_info_type of the specified debug format, for error messages.
Can be used only for individual debug format types.  */
-- 
2.41.0



[PATCH 5/5] Output S_COMPILE3 symbol in CodeView debug section

2023-10-22 Thread Mark Harmstone
Outputs the S_COMPILE3 symbol in the CodeView .debug$S debug section.
The DEBUG_S_SYMBOLS block added here makes up pretty much everything
that isn't data structures or line numbers; we add the S_COMPILE3 symbol
here to start it off.

This is a descriptive bit, the most interesting part of which is the
version of the compiler used.
---
 gcc/dwarf2codeview.cc | 126 ++
 1 file changed, 126 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 4dfc0300177..a33be036951 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -37,14 +37,25 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13   4
 
+#define DEBUG_S_SYMBOLS0xf1
 #define DEBUG_S_LINES  0xf2
 #define DEBUG_S_STRINGTABLE 0xf3
 #define DEBUG_S_FILECHKSMS  0xf4
 
 #define CHKSUM_TYPE_MD51
 
+#define S_COMPILE3 0x113c
+
+#define CV_CFL_80386   0x03
+#define CV_CFL_X64 0xD0
+
+#define CV_CFL_C   0x00
+#define CV_CFL_CXX 0x01
+
 #define LINE_LABEL "Lcvline"
 #define END_FUNC_LABEL "Lcvendfunc"
+#define SYMBOL_START_LABEL "Lcvsymstart"
+#define SYMBOL_END_LABEL   "Lcvsymend"
 
 #define HASH_SIZE 16
 
@@ -118,6 +129,7 @@ struct codeview_function
 
 static unsigned int line_label_num;
 static unsigned int func_label_num;
+static unsigned int sym_label_num;
 static codeview_source_file *files, *last_file;
 static unsigned int num_files;
 static uint32_t string_offset = 1;
@@ -590,6 +602,119 @@ codeview_end_epilogue (void)
 }
 }
 
+/* Return the CodeView constant for the selected architecture.  */
+
+static uint16_t
+target_processor (void)
+{
+  if (TARGET_64BIT)
+return CV_CFL_X64;
+  else
+return CV_CFL_80386;
+}
+
+/* Return the CodeView constant for the language being used.  */
+
+static uint32_t
+language_constant (void)
+{
+  const char *language_string = lang_hooks.name;
+
+  if (startswith (language_string, "GNU C++"))
+return CV_CFL_CXX;
+  else if (startswith (language_string, "GNU C"))
+return CV_CFL_C;
+
+  return 0;
+}
+
+/* Write a S_COMPILE3 symbol, which records the details of the compiler
+   being used.  */
+
+static void
+write_compile3_symbol (void)
+{
+  unsigned int label_num = ++sym_label_num;
+
+  static const char compiler_name[] = "GCC ";
+
+  /* This is struct COMPILESYM3 in binutils and Microsoft's cvinfo.h:
+
+ struct COMPILESYM3
+ {
+   uint16_t length;
+   uint16_t type;
+   uint32_t flags;
+   uint16_t machine;
+   uint16_t frontend_major;
+   uint16_t frontend_minor;
+   uint16_t frontend_build;
+   uint16_t frontend_qfe;
+   uint16_t backend_major;
+   uint16_t backend_minor;
+   uint16_t backend_build;
+   uint16_t backend_qfe;
+ } ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_COMPILE3);
+  putc ('\n', asm_out_file);
+
+  /* Microsoft has the flags as a bitfield, with the bottom 8 bits being the
+ language constant, and the reset being MSVC-specific stuff.  */
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, language_constant ());
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, target_processor ());
+  putc ('\n', asm_out_file);
+
+  /* Write 8 uint16_ts for the frontend and backend versions.  As with GAS, we
+ zero these, as it's easier to record the version in the compiler
+ string.  */
+  for (unsigned int i = 0; i < 8; i++)
+{
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+}
+
+  ASM_OUTPUT_ASCII (asm_out_file, compiler_name, sizeof (compiler_name) - 1);
+  ASM_OUTPUT_ASCII (asm_out_file, version_string, strlen (version_string) + 1);
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
+/* Write the CodeView symbols into the .debug$S section.  */
+
+static void
+write_codeview_symbols (void)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_SYMBOLS);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_syms_end - %LLcv_syms_start\n");
+
+  asm_fprintf (asm_out_file, "%LLcv_syms_start:\n");
+
+  write_compile3_symbol ();
+
+  asm_fprintf (asm_out_file, "%LLcv_syms_end:\n");
+}
+
 /* Finish CodeView debug info emission.  */
 
 void
@@ -604,4 +729,5 @@ codeview_debug_finish (void)
   write_strings_table ();
  

[PATCH 3/5] Output file checksums in CodeView section

2023-10-22 Thread Mark Harmstone
Outputs the file name and MD5 hash of the main source file into the
CodeView .debug$S section, along with that of any #include'd files.
---
 gcc/dwarf2codeview.cc | 254 ++
 gcc/dwarf2codeview.h  |   1 +
 gcc/dwarf2out.cc  |   3 +
 3 files changed, 258 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index e2bfdf8efeb..d93ba1ed668 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -37,6 +37,257 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13   4
 
+#define DEBUG_S_STRINGTABLE 0xf3
+#define DEBUG_S_FILECHKSMS  0xf4
+
+#define CHKSUM_TYPE_MD51
+
+#define HASH_SIZE 16
+
+struct codeview_string
+{
+  codeview_string *next;
+  uint32_t offset;
+  char *string;
+};
+
+struct string_hasher : free_ptr_hash 
+{
+  typedef const char *compare_type;
+
+  static hashval_t hash (const codeview_string *x)
+  {
+return htab_hash_string (x->string);
+  }
+
+  static bool equal (const codeview_string *x, const char *y)
+  {
+return !strcmp (x->string, y);
+  }
+
+  static void mark_empty (codeview_string *x)
+  {
+if (x->string)
+  {
+   free (x->string);
+   x->string = NULL;
+  }
+  }
+
+  static void remove (codeview_string *&x)
+  {
+free (x->string);
+  }
+};
+
+struct codeview_source_file
+{
+  codeview_source_file *next;
+  unsigned int file_num;
+  uint32_t string_offset;
+  char *filename;
+  uint8_t hash[HASH_SIZE];
+};
+
+static codeview_source_file *files, *last_file;
+static unsigned int num_files;
+static uint32_t string_offset = 1;
+static hash_table *strings_htab;
+static codeview_string *strings, *last_string;
+
+/* Adds string to the string table, returning its offset.  If already present,
+   this returns the offset of the existing string.  */
+
+static uint32_t
+add_string (const char *string)
+{
+  codeview_string **slot;
+  codeview_string *s;
+  size_t len;
+
+  if (!strings_htab)
+strings_htab = new hash_table (10);
+
+  slot = strings_htab->find_slot_with_hash (string, htab_hash_string (string),
+   INSERT);
+
+  if (*slot)
+return (*slot)->offset;
+
+  s = (codeview_string *) xmalloc (sizeof (codeview_string));
+  len = strlen (string);
+
+  s->next = NULL;
+
+  s->offset = string_offset;
+  string_offset += len + 1;
+
+  s->string = xstrdup (string);
+
+  if (last_string)
+last_string->next = s;
+  else
+strings = s;
+
+  last_string = s;
+
+  *slot = s;
+
+  return s->offset;
+}
+
+/* A new source file has been encountered - record the details and calculate
+   its hash.  */
+
+void
+codeview_start_source_file (const char *filename)
+{
+  codeview_source_file *sf;
+  char *path;
+  uint32_t string_offset;
+  FILE *f;
+
+  path = lrealpath (filename);
+  string_offset = add_string (path);
+  free (path);
+
+  sf = files;
+  while (sf)
+{
+  if (sf->string_offset == string_offset)
+   return;
+
+  sf = sf->next;
+}
+
+  sf = (codeview_source_file *) xmalloc (sizeof (codeview_source_file));
+  sf->next = NULL;
+  sf->file_num = num_files;
+  sf->string_offset = string_offset;
+  sf->filename = xstrdup (filename);
+
+  f = fopen (filename, "r");
+  if (!f)
+internal_error ("could not open %s for reading", filename);
+
+  if (md5_stream (f, sf->hash))
+{
+  fclose (f);
+  internal_error ("md5_stream failed");
+}
+
+  fclose (f);
+
+  if (last_file)
+last_file->next = sf;
+  else
+files = sf;
+
+  last_file = sf;
+  num_files++;
+}
+
+/* Write out the strings table into the .debug$S section.  The linker will
+   parse this, and handle the deduplication and hashing for all the object
+   files.  */
+
+static void
+write_strings_table (void)
+{
+  codeview_string *string;
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_STRINGTABLE);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_strings_end - %LLcv_strings_start\n");
+
+  asm_fprintf (asm_out_file, "%LLcv_strings_start:\n");
+
+  /* The first entry is always an empty string.  */
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  string = strings;
+  while (string)
+{
+  ASM_OUTPUT_ASCII (asm_out_file, string->string,
+   strlen (string->string) + 1);
+
+  string = string->next;
+}
+
+  delete strings_htab;
+
+  asm_fprintf (asm_out_file, "%LLcv_strings_end:\n");
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+}
+
+/* Write out the file checksums data into the .debug$S section.  */
+
+static void
+write_source_files (void)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_FILECHKSMS);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%LLcv_file

[PATCH 2/5] Support for CodeView debugging format

2023-10-22 Thread Mark Harmstone
This patch and the following add initial support for Microsoft's
CodeView debugging format, as used by MSVC, to mingw targets.

Note that you will need a recent version of binutils for this to be
useful. The best way to view the output is to run Microsoft's
cvdump.exe, found in their microsoft-pdb repo on GitHub, against the
object files.
---
 gcc/Makefile.in   |  2 +
 gcc/config/i386/cygming.h |  2 +
 gcc/dwarf2codeview.cc | 50 +++
 gcc/dwarf2codeview.h  | 30 +++
 gcc/dwarf2out.cc  |  4 ++
 gcc/flag-types.h  |  3 ++
 gcc/flags.h   |  4 ++
 gcc/opts.cc   | 23 +++--
 .../gcc.dg/debug/codeview/codeview-1.c|  6 +++
 .../gcc.dg/debug/codeview/codeview.exp| 48 ++
 gcc/toplev.cc |  4 ++
 11 files changed, 171 insertions(+), 5 deletions(-)
 create mode 100644 gcc/dwarf2codeview.cc
 create mode 100644 gcc/dwarf2codeview.h
 create mode 100644 gcc/testsuite/gcc.dg/debug/codeview/codeview-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/codeview/codeview.exp

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a25a1e32fbc..d011946379d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1428,6 +1428,7 @@ OBJS = \
dumpfile.o \
dwarf2asm.o \
dwarf2cfi.o \
+   dwarf2codeview.o \
dwarf2ctf.o \
dwarf2out.o \
early-remat.o \
@@ -2794,6 +2795,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h 
$(srcdir)/coretypes.h \
   $(srcdir)/dwarf2out.h \
   $(srcdir)/dwarf2asm.cc \
   $(srcdir)/dwarf2cfi.cc \
+  $(srcdir)/dwarf2codeview.cc \
   $(srcdir)/dwarf2ctf.cc \
   $(srcdir)/dwarf2out.cc \
   $(srcdir)/ctfc.h \
diff --git a/gcc/config/i386/cygming.h b/gcc/config/i386/cygming.h
index d539f8d0699..a141462133b 100644
--- a/gcc/config/i386/cygming.h
+++ b/gcc/config/i386/cygming.h
@@ -20,6 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 
 #define DWARF2_DEBUGGING_INFO 1
 
+#define CODEVIEW_DEBUGGING_INFO 1
+
 #undef PREFERRED_DEBUGGING_TYPE
 #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
 
diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
new file mode 100644
index 000..e2bfdf8efeb
--- /dev/null
+++ b/gcc/dwarf2codeview.cc
@@ -0,0 +1,50 @@
+/* Generate CodeView debugging info from the GCC DWARF.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+.  */
+
+/* See gas/codeview.h in binutils for more about the constants and structs
+   listed below.  References to Microsoft files refer to Microsoft's PDB
+   repository: https://github.com/microsoft/microsoft-pdb.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "output.h"
+#include "errors.h"
+#include "md5.h"
+#include "function.h"
+#include "version.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "dwarf2out.h"
+#include "dwarf2codeview.h"
+
+#define CV_SIGNATURE_C13   4
+
+/* Finish CodeView debug info emission.  */
+
+void
+codeview_debug_finish (void)
+{
+  targetm.asm_out.named_section (".debug$S", SECTION_DEBUG, NULL);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, CV_SIGNATURE_C13);
+  putc ('\n', asm_out_file);
+}
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
new file mode 100644
index 000..efda148eb49
--- /dev/null
+++ b/gcc/dwarf2codeview.h
@@ -0,0 +1,30 @@
+/* dwarf2codeview.h - DWARF interface for CodeView generation.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+.  */
+
+#ifndef GCC_DWARF2CODEVIEW_H
+#define GCC_DWARF2

[PATCH 4/5] Output line numbers in CodeView section

2023-10-22 Thread Mark Harmstone
Outputs the DEBUG_S_LINES block in the CodeView .debug$S section, which
maps between line numbers and addresses.

You'll need a fairly recent version of GAS for the .secidx directive to
be recognized.
---
 gcc/dwarf2codeview.cc | 303 ++
 gcc/dwarf2codeview.h  |   3 +
 gcc/dwarf2out.cc  |   9 ++
 gcc/opts.cc   |   2 +-
 4 files changed, 316 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index d93ba1ed668..4dfc0300177 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -37,11 +37,15 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13   4
 
+#define DEBUG_S_LINES  0xf2
 #define DEBUG_S_STRINGTABLE 0xf3
 #define DEBUG_S_FILECHKSMS  0xf4
 
 #define CHKSUM_TYPE_MD51
 
+#define LINE_LABEL "Lcvline"
+#define END_FUNC_LABEL "Lcvendfunc"
+
 #define HASH_SIZE 16
 
 struct codeview_string
@@ -89,11 +93,128 @@ struct codeview_source_file
   uint8_t hash[HASH_SIZE];
 };
 
+struct codeview_line
+{
+  codeview_line *next;
+  unsigned int line_no;
+  unsigned int label_num;
+};
+
+struct codeview_line_block
+{
+  codeview_line_block *next;
+  uint32_t file_id;
+  unsigned int num_lines;
+  codeview_line *lines, *last_line;
+};
+
+struct codeview_function
+{
+  codeview_function *next;
+  function *func;
+  unsigned int end_label;
+  codeview_line_block *blocks, *last_block;
+};
+
+static unsigned int line_label_num;
+static unsigned int func_label_num;
 static codeview_source_file *files, *last_file;
 static unsigned int num_files;
 static uint32_t string_offset = 1;
 static hash_table *strings_htab;
 static codeview_string *strings, *last_string;
+static codeview_function *funcs, *last_func;
+static const char* last_filename;
+static uint32_t last_file_id;
+
+/* Record new line number against the current function.  */
+
+void
+codeview_source_line (unsigned int line_no, const char *filename)
+{
+  codeview_line *l;
+  uint32_t file_id = last_file_id;
+  unsigned int label_num = ++line_label_num;
+
+  targetm.asm_out.internal_label (asm_out_file, LINE_LABEL, label_num);
+
+  if (!last_func || last_func->func != cfun)
+{
+  codeview_function *f = (codeview_function *)
+   xmalloc (sizeof (codeview_function));
+
+  f->next = NULL;
+  f->func = cfun;
+  f->end_label = 0;
+  f->blocks = f->last_block = NULL;
+
+  if (!funcs)
+   funcs = f;
+  else
+   last_func->next = f;
+
+  last_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 (!last_func->last_block || last_func->last_block->file_id != file_id)
+{
+  codeview_line_block *b;
+
+  b = (codeview_line_block *) xmalloc (sizeof (codeview_line_block));
+
+  b->next = NULL;
+  b->file_id = file_id;
+  b->num_lines = 0;
+  b->lines = b->last_line = NULL;
+
+  if (!last_func->blocks)
+   last_func->blocks = b;
+  else
+   last_func->last_block->next = b;
+
+  last_func->last_block = b;
+}
+
+  if (last_func->last_block->last_line
+&& last_func->last_block->last_line->line_no == line_no)
+return;
+
+  l = (codeview_line *) xmalloc (sizeof (codeview_line));
+
+  l->next = NULL;
+  l->line_no = line_no;
+  l->label_num = label_num;
+
+  if (!last_func->last_block->lines)
+last_func->last_block->lines = l;
+  else
+last_func->last_block->last_line->next = l;
+
+  last_func->last_block->last_line = l;
+  last_func->last_block->num_lines++;
+}
 
 /* Adds string to the string table, returning its offset.  If already present,
this returns the offset of the existing string.  */
@@ -288,6 +409,187 @@ write_source_files (void)
   asm_fprintf (asm_out_file, "%LLcv_filechksms_end:\n");
 }
 
+/* Write out the line number information for each function into the
+   .debug$S section.  */
+
+static void
+write_line_numbers (void)
+{
+  unsigned int func_num = 0;
+
+  while (funcs)
+{
+  codeview_function *next = funcs->next;
+  unsigned int first_label_num;
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_LINES);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_lines%u_end - %LLcv_lines%u_start\n",
+  func_num, func_num);
+
+  asm_fprintf (asm_out_file, "%LLcv_lines%u_start:\n",

Re: [PATCH 2/5] Support for CodeView debugging format

2023-10-26 Thread Mark Harmstone

On 26/10/23 10:35, Richard Biener wrote:

On Mon, Oct 23, 2023 at 2:57 AM Mark Harmstone  wrote:


This patch and the following add initial support for Microsoft's
CodeView debugging format, as used by MSVC, to mingw targets.


A high-level question - it seems there's almost no information in the
codeview sections,
so is that debug format even inferior to STABS?  Is it even used with
contemporary
toolchains or is DWARF a thing with MSVC?


This isn't comprehensive, I'm just adding line number info first. CodeView is as
fully featured as DWARF.


If CodeView is as full-featured as DWARF you are going to run into issues with
how we handle LTO given at dwarf2out_finish time all the DWARF for types and
declarations is "gone" (to disk).  For that post-processing the binary would be
much easier.


Thanks, I'll bear that in mind once I get to coding the bits for type 
declarations.
Types are stored in the .debug$T section, as opposed to the .debug$S section 
dealt
with here, so it might turn out that we'll need to deal with the two sections in
different ways.

Mark


[PATCH v2 0/4] CodeView patches

2023-10-30 Thread Mark Harmstone
Changes from initial version:

* First patch now accepted
* Added #ifdefs to avoid compilation failures on other targets



[PATCH v2 3/4] Output line numbers in CodeView section

2023-10-30 Thread Mark Harmstone
Outputs the DEBUG_S_LINES block in the CodeView .debug$S section, which
maps between line numbers and addresses.

You'll need a fairly recent version of GAS for the .secidx directive to
be recognized.
---
 gcc/dwarf2codeview.cc | 303 ++
 gcc/dwarf2codeview.h  |   3 +
 gcc/dwarf2out.cc  |  15 +++
 gcc/opts.cc   |   2 +-
 4 files changed, 322 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index da8315310b5..9c69ebf8998 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -39,11 +39,15 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13   4
 
+#define DEBUG_S_LINES  0xf2
 #define DEBUG_S_STRINGTABLE 0xf3
 #define DEBUG_S_FILECHKSMS  0xf4
 
 #define CHKSUM_TYPE_MD51
 
+#define LINE_LABEL "Lcvline"
+#define END_FUNC_LABEL "Lcvendfunc"
+
 #define HASH_SIZE 16
 
 struct codeview_string
@@ -91,11 +95,128 @@ struct codeview_source_file
   uint8_t hash[HASH_SIZE];
 };
 
+struct codeview_line
+{
+  codeview_line *next;
+  unsigned int line_no;
+  unsigned int label_num;
+};
+
+struct codeview_line_block
+{
+  codeview_line_block *next;
+  uint32_t file_id;
+  unsigned int num_lines;
+  codeview_line *lines, *last_line;
+};
+
+struct codeview_function
+{
+  codeview_function *next;
+  function *func;
+  unsigned int end_label;
+  codeview_line_block *blocks, *last_block;
+};
+
+static unsigned int line_label_num;
+static unsigned int func_label_num;
 static codeview_source_file *files, *last_file;
 static unsigned int num_files;
 static uint32_t string_offset = 1;
 static hash_table *strings_htab;
 static codeview_string *strings, *last_string;
+static codeview_function *funcs, *last_func;
+static const char* last_filename;
+static uint32_t last_file_id;
+
+/* Record new line number against the current function.  */
+
+void
+codeview_source_line (unsigned int line_no, const char *filename)
+{
+  codeview_line *l;
+  uint32_t file_id = last_file_id;
+  unsigned int label_num = ++line_label_num;
+
+  targetm.asm_out.internal_label (asm_out_file, LINE_LABEL, label_num);
+
+  if (!last_func || last_func->func != cfun)
+{
+  codeview_function *f = (codeview_function *)
+   xmalloc (sizeof (codeview_function));
+
+  f->next = NULL;
+  f->func = cfun;
+  f->end_label = 0;
+  f->blocks = f->last_block = NULL;
+
+  if (!funcs)
+   funcs = f;
+  else
+   last_func->next = f;
+
+  last_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 (!last_func->last_block || last_func->last_block->file_id != file_id)
+{
+  codeview_line_block *b;
+
+  b = (codeview_line_block *) xmalloc (sizeof (codeview_line_block));
+
+  b->next = NULL;
+  b->file_id = file_id;
+  b->num_lines = 0;
+  b->lines = b->last_line = NULL;
+
+  if (!last_func->blocks)
+   last_func->blocks = b;
+  else
+   last_func->last_block->next = b;
+
+  last_func->last_block = b;
+}
+
+  if (last_func->last_block->last_line
+&& last_func->last_block->last_line->line_no == line_no)
+return;
+
+  l = (codeview_line *) xmalloc (sizeof (codeview_line));
+
+  l->next = NULL;
+  l->line_no = line_no;
+  l->label_num = label_num;
+
+  if (!last_func->last_block->lines)
+last_func->last_block->lines = l;
+  else
+last_func->last_block->last_line->next = l;
+
+  last_func->last_block->last_line = l;
+  last_func->last_block->num_lines++;
+}
 
 /* Adds string to the string table, returning its offset.  If already present,
this returns the offset of the existing string.  */
@@ -290,6 +411,187 @@ write_source_files (void)
   asm_fprintf (asm_out_file, "%LLcv_filechksms_end:\n");
 }
 
+/* Write out the line number information for each function into the
+   .debug$S section.  */
+
+static void
+write_line_numbers (void)
+{
+  unsigned int func_num = 0;
+
+  while (funcs)
+{
+  codeview_function *next = funcs->next;
+  unsigned int first_label_num;
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_LINES);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_lines%u_end - %LLcv_lines%u_start\n",
+  func_num, func_num);
+
+  asm_fprintf (asm_out_file, "%LLcv_lines%u_start:\n"

[PATCH v4 4/4] Output S_COMPILE3 symbol in CodeView debug section

2023-10-30 Thread Mark Harmstone
Outputs the S_COMPILE3 symbol in the CodeView .debug$S debug section.
The DEBUG_S_SYMBOLS block added here makes up pretty much everything
that isn't data structures or line numbers; we add the S_COMPILE3 symbol
here to start it off.

This is a descriptive bit, the most interesting part of which is the
version of the compiler used.
---
 gcc/dwarf2codeview.cc | 126 ++
 1 file changed, 126 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 9c69ebf8998..db776d79be4 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -39,14 +39,25 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13   4
 
+#define DEBUG_S_SYMBOLS0xf1
 #define DEBUG_S_LINES  0xf2
 #define DEBUG_S_STRINGTABLE 0xf3
 #define DEBUG_S_FILECHKSMS  0xf4
 
 #define CHKSUM_TYPE_MD51
 
+#define S_COMPILE3 0x113c
+
+#define CV_CFL_80386   0x03
+#define CV_CFL_X64 0xD0
+
+#define CV_CFL_C   0x00
+#define CV_CFL_CXX 0x01
+
 #define LINE_LABEL "Lcvline"
 #define END_FUNC_LABEL "Lcvendfunc"
+#define SYMBOL_START_LABEL "Lcvsymstart"
+#define SYMBOL_END_LABEL   "Lcvsymend"
 
 #define HASH_SIZE 16
 
@@ -120,6 +131,7 @@ struct codeview_function
 
 static unsigned int line_label_num;
 static unsigned int func_label_num;
+static unsigned int sym_label_num;
 static codeview_source_file *files, *last_file;
 static unsigned int num_files;
 static uint32_t string_offset = 1;
@@ -592,6 +604,119 @@ codeview_end_epilogue (void)
 }
 }
 
+/* Return the CodeView constant for the selected architecture.  */
+
+static uint16_t
+target_processor (void)
+{
+  if (TARGET_64BIT)
+return CV_CFL_X64;
+  else
+return CV_CFL_80386;
+}
+
+/* Return the CodeView constant for the language being used.  */
+
+static uint32_t
+language_constant (void)
+{
+  const char *language_string = lang_hooks.name;
+
+  if (startswith (language_string, "GNU C++"))
+return CV_CFL_CXX;
+  else if (startswith (language_string, "GNU C"))
+return CV_CFL_C;
+
+  return 0;
+}
+
+/* Write a S_COMPILE3 symbol, which records the details of the compiler
+   being used.  */
+
+static void
+write_compile3_symbol (void)
+{
+  unsigned int label_num = ++sym_label_num;
+
+  static const char compiler_name[] = "GCC ";
+
+  /* This is struct COMPILESYM3 in binutils and Microsoft's cvinfo.h:
+
+ struct COMPILESYM3
+ {
+   uint16_t length;
+   uint16_t type;
+   uint32_t flags;
+   uint16_t machine;
+   uint16_t frontend_major;
+   uint16_t frontend_minor;
+   uint16_t frontend_build;
+   uint16_t frontend_qfe;
+   uint16_t backend_major;
+   uint16_t backend_minor;
+   uint16_t backend_build;
+   uint16_t backend_qfe;
+ } ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_COMPILE3);
+  putc ('\n', asm_out_file);
+
+  /* Microsoft has the flags as a bitfield, with the bottom 8 bits being the
+ language constant, and the reset being MSVC-specific stuff.  */
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, language_constant ());
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, target_processor ());
+  putc ('\n', asm_out_file);
+
+  /* Write 8 uint16_ts for the frontend and backend versions.  As with GAS, we
+ zero these, as it's easier to record the version in the compiler
+ string.  */
+  for (unsigned int i = 0; i < 8; i++)
+{
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+}
+
+  ASM_OUTPUT_ASCII (asm_out_file, compiler_name, sizeof (compiler_name) - 1);
+  ASM_OUTPUT_ASCII (asm_out_file, version_string, strlen (version_string) + 1);
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
+/* Write the CodeView symbols into the .debug$S section.  */
+
+static void
+write_codeview_symbols (void)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_SYMBOLS);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_syms_end - %LLcv_syms_start\n");
+
+  asm_fprintf (asm_out_file, "%LLcv_syms_start:\n");
+
+  write_compile3_symbol ();
+
+  asm_fprintf (asm_out_file, "%LLcv_syms_end:\n");
+}
+
 /* Finish CodeView debug info emission.  */
 
 void
@@ -606,6 +731,7 @@ codeview_debug_finish (void)
   write_strings_table ();
  

[PATCH v2 1/4] Support for CodeView debugging format

2023-10-30 Thread Mark Harmstone
This patch and the following add initial support for Microsoft's
CodeView debugging format, as used by MSVC, to mingw targets.

Note that you will need a recent version of binutils for this to be
useful. The best way to view the output is to run Microsoft's
cvdump.exe, found in their microsoft-pdb repo on GitHub, against the
object files.
---
 gcc/Makefile.in   |  2 +
 gcc/config/i386/cygming.h |  2 +
 gcc/dwarf2codeview.cc | 54 +++
 gcc/dwarf2codeview.h  | 30 +++
 gcc/dwarf2out.cc  |  6 +++
 gcc/flag-types.h  |  3 ++
 gcc/flags.h   |  4 ++
 gcc/opts.cc   | 23 ++--
 .../gcc.dg/debug/codeview/codeview-1.c|  6 +++
 .../gcc.dg/debug/codeview/codeview.exp| 48 +
 gcc/toplev.cc |  4 ++
 11 files changed, 177 insertions(+), 5 deletions(-)
 create mode 100644 gcc/dwarf2codeview.cc
 create mode 100644 gcc/dwarf2codeview.h
 create mode 100644 gcc/testsuite/gcc.dg/debug/codeview/codeview-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/codeview/codeview.exp

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 91d6bfbea4d..b260fe12c08 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1430,6 +1430,7 @@ OBJS = \
dumpfile.o \
dwarf2asm.o \
dwarf2cfi.o \
+   dwarf2codeview.o \
dwarf2ctf.o \
dwarf2out.o \
early-remat.o \
@@ -2800,6 +2801,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h 
$(srcdir)/coretypes.h \
   $(srcdir)/dwarf2out.h \
   $(srcdir)/dwarf2asm.cc \
   $(srcdir)/dwarf2cfi.cc \
+  $(srcdir)/dwarf2codeview.cc \
   $(srcdir)/dwarf2ctf.cc \
   $(srcdir)/dwarf2out.cc \
   $(srcdir)/ctfc.h \
diff --git a/gcc/config/i386/cygming.h b/gcc/config/i386/cygming.h
index d539f8d0699..a141462133b 100644
--- a/gcc/config/i386/cygming.h
+++ b/gcc/config/i386/cygming.h
@@ -20,6 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 
 #define DWARF2_DEBUGGING_INFO 1
 
+#define CODEVIEW_DEBUGGING_INFO 1
+
 #undef PREFERRED_DEBUGGING_TYPE
 #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
 
diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
new file mode 100644
index 000..f08f5d55ad7
--- /dev/null
+++ b/gcc/dwarf2codeview.cc
@@ -0,0 +1,54 @@
+/* Generate CodeView debugging info from the GCC DWARF.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+.  */
+
+/* See gas/codeview.h in binutils for more about the constants and structs
+   listed below.  References to Microsoft files refer to Microsoft's PDB
+   repository: https://github.com/microsoft/microsoft-pdb.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "output.h"
+#include "errors.h"
+#include "md5.h"
+#include "function.h"
+#include "version.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "dwarf2out.h"
+#include "dwarf2codeview.h"
+
+#ifdef CODEVIEW_DEBUGGING_INFO
+
+#define CV_SIGNATURE_C13   4
+
+/* Finish CodeView debug info emission.  */
+
+void
+codeview_debug_finish (void)
+{
+  targetm.asm_out.named_section (".debug$S", SECTION_DEBUG, NULL);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, CV_SIGNATURE_C13);
+  putc ('\n', asm_out_file);
+}
+
+#endif
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
new file mode 100644
index 000..efda148eb49
--- /dev/null
+++ b/gcc/dwarf2codeview.h
@@ -0,0 +1,30 @@
+/* dwarf2codeview.h - DWARF interface for CodeView generation.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+.  */
+
+#ifnd

[PATCH v2 2/4] Output file checksums in CodeView section

2023-10-30 Thread Mark Harmstone
Outputs the file name and MD5 hash of the main source file into the
CodeView .debug$S section, along with that of any #include'd files.
---
 gcc/dwarf2codeview.cc | 254 ++
 gcc/dwarf2codeview.h  |   1 +
 gcc/dwarf2out.cc  |   5 +
 3 files changed, 260 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index f08f5d55ad7..da8315310b5 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -39,6 +39,257 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13   4
 
+#define DEBUG_S_STRINGTABLE 0xf3
+#define DEBUG_S_FILECHKSMS  0xf4
+
+#define CHKSUM_TYPE_MD51
+
+#define HASH_SIZE 16
+
+struct codeview_string
+{
+  codeview_string *next;
+  uint32_t offset;
+  char *string;
+};
+
+struct string_hasher : free_ptr_hash 
+{
+  typedef const char *compare_type;
+
+  static hashval_t hash (const codeview_string *x)
+  {
+return htab_hash_string (x->string);
+  }
+
+  static bool equal (const codeview_string *x, const char *y)
+  {
+return !strcmp (x->string, y);
+  }
+
+  static void mark_empty (codeview_string *x)
+  {
+if (x->string)
+  {
+   free (x->string);
+   x->string = NULL;
+  }
+  }
+
+  static void remove (codeview_string *&x)
+  {
+free (x->string);
+  }
+};
+
+struct codeview_source_file
+{
+  codeview_source_file *next;
+  unsigned int file_num;
+  uint32_t string_offset;
+  char *filename;
+  uint8_t hash[HASH_SIZE];
+};
+
+static codeview_source_file *files, *last_file;
+static unsigned int num_files;
+static uint32_t string_offset = 1;
+static hash_table *strings_htab;
+static codeview_string *strings, *last_string;
+
+/* Adds string to the string table, returning its offset.  If already present,
+   this returns the offset of the existing string.  */
+
+static uint32_t
+add_string (const char *string)
+{
+  codeview_string **slot;
+  codeview_string *s;
+  size_t len;
+
+  if (!strings_htab)
+strings_htab = new hash_table (10);
+
+  slot = strings_htab->find_slot_with_hash (string, htab_hash_string (string),
+   INSERT);
+
+  if (*slot)
+return (*slot)->offset;
+
+  s = (codeview_string *) xmalloc (sizeof (codeview_string));
+  len = strlen (string);
+
+  s->next = NULL;
+
+  s->offset = string_offset;
+  string_offset += len + 1;
+
+  s->string = xstrdup (string);
+
+  if (last_string)
+last_string->next = s;
+  else
+strings = s;
+
+  last_string = s;
+
+  *slot = s;
+
+  return s->offset;
+}
+
+/* A new source file has been encountered - record the details and calculate
+   its hash.  */
+
+void
+codeview_start_source_file (const char *filename)
+{
+  codeview_source_file *sf;
+  char *path;
+  uint32_t string_offset;
+  FILE *f;
+
+  path = lrealpath (filename);
+  string_offset = add_string (path);
+  free (path);
+
+  sf = files;
+  while (sf)
+{
+  if (sf->string_offset == string_offset)
+   return;
+
+  sf = sf->next;
+}
+
+  sf = (codeview_source_file *) xmalloc (sizeof (codeview_source_file));
+  sf->next = NULL;
+  sf->file_num = num_files;
+  sf->string_offset = string_offset;
+  sf->filename = xstrdup (filename);
+
+  f = fopen (filename, "r");
+  if (!f)
+internal_error ("could not open %s for reading", filename);
+
+  if (md5_stream (f, sf->hash))
+{
+  fclose (f);
+  internal_error ("md5_stream failed");
+}
+
+  fclose (f);
+
+  if (last_file)
+last_file->next = sf;
+  else
+files = sf;
+
+  last_file = sf;
+  num_files++;
+}
+
+/* Write out the strings table into the .debug$S section.  The linker will
+   parse this, and handle the deduplication and hashing for all the object
+   files.  */
+
+static void
+write_strings_table (void)
+{
+  codeview_string *string;
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_STRINGTABLE);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_strings_end - %LLcv_strings_start\n");
+
+  asm_fprintf (asm_out_file, "%LLcv_strings_start:\n");
+
+  /* The first entry is always an empty string.  */
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  string = strings;
+  while (string)
+{
+  ASM_OUTPUT_ASCII (asm_out_file, string->string,
+   strlen (string->string) + 1);
+
+  string = string->next;
+}
+
+  delete strings_htab;
+
+  asm_fprintf (asm_out_file, "%LLcv_strings_end:\n");
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+}
+
+/* Write out the file checksums data into the .debug$S section.  */
+
+static void
+write_source_files (void)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_FILECHKSMS);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%LLcv_file

[PATCH] Add -gcodeview option

2022-10-23 Thread Mark Harmstone
Both current lld and the next version of ld have an option -pdb, which
creates a PDB file which Microsoft's debuggers can use. This patch adds
a -gcodeview option, which passes this to the linker.

I do intend to expand this so it also creates the .debug$S and .debug$T
sections which would make this useful - I submitted patches for this a
while back, but they need to be rewritten to parse the DWARF DIEs rather
than using debug_hooks.

Clang also has -gcodeview, but AFAICS only uses it for .debug$S and
.debug$T, and doesn't use it for linker options (though IMO it probably
should).

---
 gcc/common.opt  | 4 
 gcc/doc/invoke.texi | 7 +++
 gcc/gcc.cc  | 4 
 gcc/opts.cc | 3 +++
 4 files changed, 18 insertions(+)

diff --git a/gcc/common.opt b/gcc/common.opt
index 8a0dafc522d..77103f961d8 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -3253,6 +3253,10 @@ gas-locview-support
 Common Driver Var(dwarf2out_as_locview_support)
 Assume assembler support for view in (DWARF2+) .loc directives.
 
+gcodeview
+Common Driver JoinedOrMissing
+Generate debug information in CodeView format.
+
 gcoff
 Common Driver WarnRemoved
 Does nothing.  Preserved for backward compatibility.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ff6c338bedb..2d29fd2611d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -483,6 +483,7 @@ Objective-C and Objective-C++ Dialects}.
 -gstabs  -gstabs+  -gstrict-dwarf  -gno-strict-dwarf @gol
 -gas-loc-support  -gno-as-loc-support @gol
 -gas-locview-support  -gno-as-locview-support @gol
+-gcodeview @gol
 -gcolumn-info  -gno-column-info  -gdwarf32  -gdwarf64 @gol
 -gstatement-frontiers  -gno-statement-frontiers @gol
 -gvariable-location-views  -gno-variable-location-views @gol
@@ -10358,6 +10359,12 @@ assembler (GAS) to fail with an error.
 Produce debugging information in Alpha/VMS debug format (if that is
 supported).  This is the format used by DEBUG on Alpha/VMS systems.
 
+@item -gcodeview
+@opindex gcodeview
+Produce debugging information in CodeView debug format (if that is
+supported).  This is the format used by Microsoft Visual C++ on
+Windows.
+
 @item -g@var{level}
 @itemx -ggdb@var{level}
 @itemx -gstabs@var{level}
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index bb07cc244e3..2820f325282 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -4608,6 +4608,10 @@ driver_handle_option (struct gcc_options *opts,
   do_save = false;
   break;
 
+case OPT_gcodeview:
+  add_infile ("-pdb=", "*");
+  break;
+
 default:
   /* Various driver options need no special processing at this
 point, having been handled in a prescan above or being
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 3a89da2dd03..e2633ee5439 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -3089,6 +3089,9 @@ common_handle_option (struct gcc_options *opts,
   set_debug_level (NO_DEBUG, 2, arg, opts, opts_set, loc);
   break;
 
+case OPT_gcodeview:
+  break;
+
 case OPT_gstabs:
 case OPT_gstabs_:
   set_debug_level (DBX_DEBUG, code == OPT_gstabs_, arg, opts, opts_set,
-- 
2.37.3



Re: [PATCH] Add -gcodeview option

2022-10-24 Thread Mark Harmstone

On 24/10/22 12:08, Martin Storsjö wrote:
Hmm, what does this end up passing to the linker in the end - does it just pass "-pdb="? (What does the "*" parameter do here?) If that's the case - that sounds reasonable - assuming that if a user passes an extra -Wl,--pdb,myspecificname.pdb, that would take precedence (i.e. be passed after the compiler's default one). 


That's right. The "*" means "all languages".

Mark



Re: [PATCH] Add -gcodeview option

2022-10-26 Thread Mark Harmstone

On 25/10/22 09:21, Martin Storsjö wrote:

Btw, stylistically, should we strive towards using double dashes for the pdb option? I 
think that's the most canonical way for such getopt options (even though it doesn't make 
any practical difference). I've started trying to update various users to prefer that form 
(together with preferring -Wl,--pdb= over -Wl,--pdb,) and probably 
will send a few more.

Thanks Martin. Yes, I agree.


[PATCH v2] Add -gcodeview option

2022-10-26 Thread Mark Harmstone
Changed to double dashes as per
https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604287.html.

---
 gcc/common.opt  | 4 
 gcc/doc/invoke.texi | 7 +++
 gcc/gcc.cc  | 4 
 gcc/opts.cc | 3 +++
 4 files changed, 18 insertions(+)

diff --git a/gcc/common.opt b/gcc/common.opt
index 8a0dafc522d..77103f961d8 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -3253,6 +3253,10 @@ gas-locview-support
 Common Driver Var(dwarf2out_as_locview_support)
 Assume assembler support for view in (DWARF2+) .loc directives.
 
+gcodeview
+Common Driver JoinedOrMissing
+Generate debug information in CodeView format.
+
 gcoff
 Common Driver WarnRemoved
 Does nothing.  Preserved for backward compatibility.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ff6c338bedb..2d29fd2611d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -483,6 +483,7 @@ Objective-C and Objective-C++ Dialects}.
 -gstabs  -gstabs+  -gstrict-dwarf  -gno-strict-dwarf @gol
 -gas-loc-support  -gno-as-loc-support @gol
 -gas-locview-support  -gno-as-locview-support @gol
+-gcodeview @gol
 -gcolumn-info  -gno-column-info  -gdwarf32  -gdwarf64 @gol
 -gstatement-frontiers  -gno-statement-frontiers @gol
 -gvariable-location-views  -gno-variable-location-views @gol
@@ -10358,6 +10359,12 @@ assembler (GAS) to fail with an error.
 Produce debugging information in Alpha/VMS debug format (if that is
 supported).  This is the format used by DEBUG on Alpha/VMS systems.
 
+@item -gcodeview
+@opindex gcodeview
+Produce debugging information in CodeView debug format (if that is
+supported).  This is the format used by Microsoft Visual C++ on
+Windows.
+
 @item -g@var{level}
 @itemx -ggdb@var{level}
 @itemx -gstabs@var{level}
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index bb07cc244e3..d3aa3deeaf7 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -4608,6 +4608,10 @@ driver_handle_option (struct gcc_options *opts,
   do_save = false;
   break;
 
+case OPT_gcodeview:
+  add_infile ("--pdb=", "*");
+  break;
+
 default:
   /* Various driver options need no special processing at this
 point, having been handled in a prescan above or being
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 3a89da2dd03..e2633ee5439 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -3089,6 +3089,9 @@ common_handle_option (struct gcc_options *opts,
   set_debug_level (NO_DEBUG, 2, arg, opts, opts_set, loc);
   break;
 
+case OPT_gcodeview:
+  break;
+
 case OPT_gstabs:
 case OPT_gstabs_:
   set_debug_level (DBX_DEBUG, code == OPT_gstabs_, arg, opts, opts_set,
-- 
2.37.4



Re: [PATCH v1 00/13] Add aarch64-w64-mingw32 target

2024-02-22 Thread Mark Harmstone

Hi all,

Seems to work for me! Nice work.

It also works nicely with EFI as well, for anyone interested:

test.c:

#include 

EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) {
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"hello, world\r\n");

return EFI_SUCCESS;
}

$ aarch64-w64-mingw32-gcc -I/usr/include/efi -nostartfiles -Wl,--subsystem,10 
-eefi_main test.c -o test.efi

Mark

On 21/2/24 17:47, Evgeny Karpov wrote:

Hello,

We would like to take your attention to the review of changes for the
new GCC target, aarch64-w64-mingw32. The new target will be
supported, tested, added to CI, and maintained by Linaro. This marks
the first of three planned patch series contributing to the GCC C
compiler's support for Windows Arm64.

1. Minimal aarch64-w64-mingw32 C implementation to cross-compile
hello-world with libgcc for Windows Arm64 using MinGW.
2. Extension of the aarch64-w64-mingw32 C implementation to
cross-compile OpenSSL, OpenBLAS, FFmpeg, and libjpeg-turbo. All
packages successfully pass tests.
3. Addition of call stack support for debugging, resolution of
optimization issues in the C compiler, and DLL export/import for the
aarch64-w64-mingw32 target.

This patch series introduces the 1st point, which involves building
hello-world for the aarch64-w64-mingw32 target. The patch depends on
the binutils changes for the aarch64-w64-mingw32 target that have
already been merged.

The binutils should include recent relocation fixes.
f87eaf8ff3995a5888c6dc4996a20c770e6bcd36
aarch64: Add new relocations and limit COFF AArch64 relocation offsets

The series is structured in a way to trivially show that it should not
affect any other targets.

In this patch, several changes have been made to support the
aarch64-w64-mingw32 target for GCC. The modifications include the
definition of the MS ABI for aarch64, adjustments to FIXED_REGISTERS
and STATIC_CHAIN_REGNUM for different ABIs, and specific definitions
for COFF format on AArch64. Additionally, the patch reuses MinGW
 types and definitions from i386, relocating them to a new
mingw folder for shared usage between both targets.

MinGW-specific options have been introduced for AArch64, along with
override options for aarch64-w64-mingw32. Builtin stack probing for
override options for aarch64-w64-mingw32. Builtin stack probing for
AArch64 has been enabled as an alternative for chkstk. Symbol name
encoding and section information handling for aarch64-w64-mingw32 have
been incorporated, and the MinGW environment has been added, which
will also be utilized for defining the Cygwin environment in the
future.

The patch includes renaming "x86 Windows Options" to "Cygwin and MinGW
Options," which now encompasses AArch64 as well. AArch64-specific
Cygwin and MinGW Options have been introduced for the unique
requirements of the AArch64 architecture.

Function type declaration and named sections support have been added.
The necessary objects for Cygwin and MinGW have been built for the
aarch64-w64-mingw32 target, and relevant files such as msformat-c.cc
and winnt-d.cc have been moved to the mingw folder for reuse in
AArch64.

Furthermore, the aarch64-w64-mingw32 target has been included in both
libatomic and libgcc, ensuring support for the AArch64 architecture
within these libraries. These changes collectively enhance the
capabilities of GCC for the specified target.

Coauthors: Zac Walker ,
Mark Harmstone   and
Ron Riddle 

Refactored, prepared, and validated by
Radek Barton  and
Evgeny Karpov 

Special thanks to the Linaro GNU toolchain team for internal review
and assistance in preparing the patch series!

Regards,
Evgeny


Zac Walker (13):
   Introduce aarch64-w64-mingw32 target
   aarch64: The aarch64-w64-mingw32 target implements the MS ABI
   aarch64: Mark x18 register as a fixed register for MS ABI
   aarch64: Add aarch64-w64-mingw32 COFF
   Reuse MinGW from i386 for AArch64
   Rename section and encoding functions from i386 which will be used in
 aarch64
   Exclude i386 functionality from aarch64 build
   aarch64: Add Cygwin and MinGW environments for AArch64
   aarch64: Add SEH to machine_function
   Rename "x86 Windows Options" to "Cygwin and MinGW Options"
   aarch64: Build and add objects for Cygwin and MinGW for AArch64
   aarch64: Add aarch64-w64-mingw32 target to libatomic
   Add aarch64-w64-mingw32 target to libgcc

  fixincludes/mkfixinc.sh   |   3 +-
  gcc/config.gcc|  47 +++--
  gcc/config/aarch64/aarch64-coff.h |  92 +
  gcc/config/aarch64/aarch64-opts.h |   7 +
  gcc/config/aarch64/aarch64-protos.h   |   5 +
  gcc/config/aarch64/aarch64.h  |  25 ++-
  gcc/config/aarch64/cygming.h  | 178 ++
  gcc/config/i386/cygming.h |  18 +-
  gcc/config/i386/cygming.opt.urls  |  30 --

Re: [PATCH v1 05/13] Reuse MinGW from i386 for AArch64

2024-02-25 Thread Mark Harmstone

On 22/2/24 11:11, Richard Earnshaw (lists) wrote:

Most of the free world has left COFF behind since several decades, so I won't 
comment on that. YMMV.

This isn't helpful.  Windows platforms use (a derivative of) COFF, so that's 
what the tools need to use when targetting that platform.


Also, there are relocation types needed for Windows programs that are supported 
in COFF but not in ELF object files.

Mark


Re: [PATCH v1 02/13] aarch64: The aarch64-w64-mingw32 target implements

2024-02-25 Thread Mark Harmstone

On 23/2/24 17:54, Andrew Pinski wrote:

There is arm64ec ABI defined for aarch64 windows which is a different
ABI from the standard windows aarch64 ABI, though I am not sure if it
supported with the patches here.
It is documented at
https://learn.microsoft.com/en-us/cpp/build/arm64ec-windows-abi-conventions?view=msvc-170
.


ARM64EC would also need a lot of work in binutils, and AFAIK no-one's been 
working on that yet.

Mark


[PATCH 1/5] Add CodeView enum cv_leaf_type

2024-06-29 Thread Mark Harmstone
Make everything more gdb-friendly by using an enum for type constants
rather than #defines.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Define.
(struct codeview_subtype): Use enum cv_leaf_type.
(struct codeview_custom_type): Use enum cv_leaf_type.
(write_lf_fieldlist): Add default to switch.
(write_custom_types): Add default to switch.
* dwarf2codeview.h (LF_MODIFIER, LF_POINTER): Undefine.
(LF_PROCEDURE, LF_ARGLIST, LF_FIELDLIST, LF_BITFIELD): Likewise.
(LF_INDEX, LF_ENUMERATE, LF_ARRAY, LF_CLASS): Likewise.
(LF_STRUCTURE, LF_UNION, LF_ENUM, LF_MEMBER, LF_CHAR): Likewise.
(LF_SHORT, LF_USHORT, LF_LONG, LF_ULONG, LF_QUADWORD): Likewise.
(LF_UQUADWORD): Likewise.
---
 gcc/dwarf2codeview.cc | 37 +++--
 gcc/dwarf2codeview.h  | 23 ---
 2 files changed, 35 insertions(+), 25 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index e8ed3713480..5155aa70139 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -70,6 +70,33 @@ along with GCC; see the file COPYING3.  If not see
 
 #define HASH_SIZE 16
 
+/* This is enum LEAF_ENUM_e in Microsoft's cvinfo.h.  */
+
+enum cv_leaf_type {
+  LF_MODIFIER = 0x1001,
+  LF_POINTER = 0x1002,
+  LF_PROCEDURE = 0x1008,
+  LF_ARGLIST = 0x1201,
+  LF_FIELDLIST = 0x1203,
+  LF_BITFIELD = 0x1205,
+  LF_INDEX = 0x1404,
+  LF_ENUMERATE = 0x1502,
+  LF_ARRAY = 0x1503,
+  LF_CLASS = 0x1504,
+  LF_STRUCTURE = 0x1505,
+  LF_UNION = 0x1506,
+  LF_ENUM = 0x1507,
+  LF_MEMBER = 0x150d,
+  LF_FUNC_ID = 0x1601,
+  LF_CHAR = 0x8000,
+  LF_SHORT = 0x8001,
+  LF_USHORT = 0x8002,
+  LF_LONG = 0x8003,
+  LF_ULONG = 0x8004,
+  LF_QUADWORD = 0x8009,
+  LF_UQUADWORD = 0x800a
+};
+
 struct codeview_string
 {
   codeview_string *next;
@@ -185,7 +212,7 @@ struct codeview_integer
 struct codeview_subtype
 {
   struct codeview_subtype *next;
-  uint16_t kind;
+  enum cv_leaf_type kind;
 
   union
   {
@@ -212,7 +239,7 @@ struct codeview_custom_type
 {
   struct codeview_custom_type *next;
   uint32_t num;
-  uint16_t kind;
+  enum cv_leaf_type kind;
 
   union
   {
@@ -1336,6 +1363,9 @@ write_lf_fieldlist (codeview_custom_type *t)
  putc ('\n', asm_out_file);
 
  break;
+
+   default:
+ break;
}
 
   t->lf_fieldlist.subtypes = next;
@@ -1790,6 +1820,9 @@ write_custom_types (void)
case LF_ARGLIST:
  write_lf_arglist (custom_types);
  break;
+
+   default:
+ break;
}
 
   free (custom_types);
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index e6ad517bf28..8fd3632e524 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -60,29 +60,6 @@ along with GCC; see the file COPYING3.  If not see
 #define MOD_const  0x1
 #define MOD_volatile   0x2
 
-/* Constants for type definitions.  */
-#define LF_MODIFIER0x1001
-#define LF_POINTER 0x1002
-#define LF_PROCEDURE   0x1008
-#define LF_ARGLIST 0x1201
-#define LF_FIELDLIST   0x1203
-#define LF_BITFIELD0x1205
-#define LF_INDEX   0x1404
-#define LF_ENUMERATE   0x1502
-#define LF_ARRAY   0x1503
-#define LF_CLASS   0x1504
-#define LF_STRUCTURE   0x1505
-#define LF_UNION   0x1506
-#define LF_ENUM0x1507
-#define LF_MEMBER  0x150d
-#define LF_CHAR0x8000
-#define LF_SHORT   0x8001
-#define LF_USHORT  0x8002
-#define LF_LONG0x8003
-#define LF_ULONG   0x8004
-#define LF_QUADWORD0x8009
-#define LF_UQUADWORD   0x800a
-
 #define CV_ACCESS_PRIVATE  1
 #define CV_ACCESS_PROTECTED2
 #define CV_ACCESS_PUBLIC   3
-- 
2.44.2



[PATCH 2/5] Add CodeView enum cv_sym_type

2024-06-29 Thread Mark Harmstone
Make everything more gdb-friendly by using an enum for symbol constants
rather than #defines.

gcc/
* dwarf2codeview.cc (S_LDATA32, S_GDATA32, S_COMPILE3): Undefine.
(enum cv_sym_type): Define.
(struct codeview_symbol): Use enum cv_sym_type.
(write_codeview_symbols): Add default to switch.
---
 gcc/dwarf2codeview.cc | 16 +++-
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 5155aa70139..921d5f41e5a 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -46,10 +46,6 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CHKSUM_TYPE_MD51
 
-#define S_LDATA32  0x110c
-#define S_GDATA32  0x110d
-#define S_COMPILE3 0x113c
-
 #define CV_CFL_80386   0x03
 #define CV_CFL_X64 0xD0
 
@@ -70,6 +66,14 @@ along with GCC; see the file COPYING3.  If not see
 
 #define HASH_SIZE 16
 
+/* This is enum SYM_ENUM_e in Microsoft's cvinfo.h.  */
+
+enum cv_sym_type {
+  S_LDATA32 = 0x110c,
+  S_GDATA32 = 0x110d,
+  S_COMPILE3 = 0x113c
+};
+
 /* This is enum LEAF_ENUM_e in Microsoft's cvinfo.h.  */
 
 enum cv_leaf_type {
@@ -168,7 +172,7 @@ struct codeview_function
 struct codeview_symbol
 {
   codeview_symbol *next;
-  uint16_t kind;
+  enum cv_sym_type kind;
 
   union
   {
@@ -983,6 +987,8 @@ write_codeview_symbols (void)
case S_GDATA32:
  write_data_symbol (sym);
  break;
+   default:
+ break;
}
 
   free (sym);
-- 
2.44.2



[PATCH 3/5] Avoid magic numbers when writing CodeView padding

2024-06-29 Thread Mark Harmstone
Adds names for the padding magic numbers to enum cv_leaf_type.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Add padding constants.
(write_cv_padding): Use names for padding constants.
---
 gcc/dwarf2codeview.cc | 11 +++
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 921d5f41e5a..71049ccf878 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -77,6 +77,9 @@ enum cv_sym_type {
 /* This is enum LEAF_ENUM_e in Microsoft's cvinfo.h.  */
 
 enum cv_leaf_type {
+  LF_PAD1 = 0xf1,
+  LF_PAD2 = 0xf2,
+  LF_PAD3 = 0xf3,
   LF_MODIFIER = 0x1001,
   LF_POINTER = 0x1002,
   LF_PROCEDURE = 0x1008,
@@ -1037,7 +1040,7 @@ write_lf_pointer (codeview_custom_type *t)
 
 /* All CodeView type definitions have to be aligned to a four-byte boundary,
so write some padding bytes if necessary.  These have to be specific values:
-   f3, f2, f1.  */
+   LF_PAD3, LF_PAD2, LF_PAD1.  */
 
 static void
 write_cv_padding (size_t padding)
@@ -1048,19 +1051,19 @@ write_cv_padding (size_t padding)
   if (padding == 3)
 {
   fputs (integer_asm_op (1, false), asm_out_file);
-  fprint_whex (asm_out_file, 0xf3);
+  fprint_whex (asm_out_file, LF_PAD3);
   putc ('\n', asm_out_file);
 }
 
   if (padding >= 2)
 {
   fputs (integer_asm_op (1, false), asm_out_file);
-  fprint_whex (asm_out_file, 0xf2);
+  fprint_whex (asm_out_file, LF_PAD2);
   putc ('\n', asm_out_file);
 }
 
   fputs (integer_asm_op (1, false), asm_out_file);
-  fprint_whex (asm_out_file, 0xf1);
+  fprint_whex (asm_out_file, LF_PAD1);
   putc ('\n', asm_out_file);
 }
 
-- 
2.44.2



[PATCH 4/5] Make sure CodeView symbols are aligned

2024-06-29 Thread Mark Harmstone
CodeView symbols have to be multiples of four bytes; add an alignment
directive to write_data_symbol to ensure this.

Note that these can be zeroes, so we can rely on GAS to do this for us;
it's only types that need f3, f2, f1 values.

gcc/
* dwarf2codeview.cc (write_data_symbol): Add alignment directive.
---
 gcc/dwarf2codeview.cc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 71049ccf878..5a33b439b14 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -958,6 +958,8 @@ write_data_symbol (codeview_symbol *s)
   ASM_OUTPUT_ASCII (asm_out_file, s->data_symbol.name,
strlen (s->data_symbol.name) + 1);
 
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 
 end:
-- 
2.44.2



[PATCH 5/5] Document return value in write_cv_integer

2024-06-29 Thread Mark Harmstone
gcc/
* dwarf2codeview.cc (write_lf_modifier): Expand upon comment.
---
 gcc/dwarf2codeview.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 5a33b439b14..df53d8bab9d 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1113,7 +1113,7 @@ write_lf_modifier (codeview_custom_type *t)
 /* Write a CodeView extensible integer.  If the value is non-negative and
< 0x8000, the value gets written directly as an uint16_t.  Otherwise, we
output two bytes for the integer type (LF_CHAR, LF_SHORT, ...), and the
-   actual value follows.  */
+   actual value follows.  Returns the total number of bytes written.  */
 
 static size_t
 write_cv_integer (codeview_integer *i)
-- 
2.44.2



[PATCH] Output CodeView function information

2024-07-13 Thread Mark Harmstone
Translate DW_TAG_subprogram DIEs into CodeView LF_FUNC_ID types and
S_GPROC32_ID / S_LPROC32_ID symbols.  ld will then transform these into
S_GPROC32 / S_LPROC32 symbols, which map addresses to unmangled function
names.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add new values.
(struct codeview_symbol): Add function to union.
(struct codeview_custom_type): Add lf_func_id to union.
(write_function): New function.
(write_codeview_symbols): Call write_function.
(write_lf_func_id): New function.
(write_custom_types): Call write_lf_func_id.
(add_function): New function.
(codeview_debug_early_finish): Call add_function.
---
 gcc/dwarf2codeview.cc | 291 +-
 1 file changed, 288 insertions(+), 3 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index df53d8bab9d..c174f320480 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "dwarf2out.h"
 #include "dwarf2codeview.h"
+#include "rtl.h"
 
 #ifdef CODEVIEW_DEBUGGING_INFO
 
@@ -71,7 +72,10 @@ along with GCC; see the file COPYING3.  If not see
 enum cv_sym_type {
   S_LDATA32 = 0x110c,
   S_GDATA32 = 0x110d,
-  S_COMPILE3 = 0x113c
+  S_COMPILE3 = 0x113c,
+  S_LPROC32_ID = 0x1146,
+  S_GPROC32_ID = 0x1147,
+  S_PROC_ID_END = 0x114f
 };
 
 /* This is enum LEAF_ENUM_e in Microsoft's cvinfo.h.  */
@@ -185,6 +189,16 @@ struct codeview_symbol
   char *name;
   dw_die_ref die;
 } data_symbol;
+struct
+{
+  uint32_t parent;
+  uint32_t end;
+  uint32_t next;
+  uint32_t type;
+  uint8_t flags;
+  char *name;
+  dw_die_ref die;
+} function;
   };
 };
 
@@ -309,6 +323,12 @@ struct codeview_custom_type
   uint32_t num_entries;
   uint32_t *args;
 } lf_arglist;
+struct
+{
+  uint32_t parent_scope;
+  uint32_t function_type;
+  char *name;
+} lf_func_id;
   };
 };
 
@@ -966,6 +986,152 @@ end:
   free (s->data_symbol.name);
 }
 
+/* Write an S_GPROC32_ID symbol, representing a global function, or an
+   S_LPROC32_ID symbol, for a static function.  */
+
+static void
+write_function (codeview_symbol *s)
+{
+  unsigned int label_num = ++sym_label_num;
+  dw_attr_node *loc_low, *loc_high;
+  const char *label_low, *label_high;
+  rtx rtx_low, rtx_high;
+
+  /* This is struct procsym in binutils and PROCSYM32 in Microsoft's cvinfo.h:
+
+  struct procsym
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint32_t parent;
+   uint32_t end;
+   uint32_t next;
+   uint32_t proc_len;
+   uint32_t debug_start;
+   uint32_t debug_end;
+   uint32_t type;
+   uint32_t offset;
+   uint16_t section;
+   uint8_t flags;
+   char name[];
+  } ATTRIBUTE_PACKED;
+  */
+
+  loc_low = get_AT (s->function.die, DW_AT_low_pc);
+  if (!loc_low)
+goto end;
+
+  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;
+
+  rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low);
+
+  loc_high = get_AT (s->function.die, DW_AT_high_pc);
+  if (!loc_high)
+goto end;
+
+  if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc)
+goto end;
+
+  label_high = loc_high->dw_attr_val.v.val_lbl_id;
+  if (!label_high)
+goto end;
+
+  rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high);
+
+  /* Output the S_GPROC32_ID / S_LPROC32_ID record.  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, s->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, s->function.parent);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, s->function.end);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, s->function.next);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  output_addr_const (asm_out_file, rtx_high);
+  fputs (" - ", asm_out_file);
+  output_addr_const (asm_out_file, rtx_low);
+  putc ('\n', asm_out_file);
+
+  /* FIXME - debug_start should be the end of the prologue, and debug_end
+the beginning of the epilogue.  Do the whole function for
+now.  */
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  output_addr_const (asm_out_file, rtx_high);
+

[PATCH 2/2] Output CodeView type information for rvalue references

2024-07-20 Thread Mark Harmstone
Translates DW_TAG_rvalue_reference_type DIEs into LF_POINTER types.

gcc/
* dwarf2codeview.cc (get_type_num_reference_type): Handle rvalue refs.
(get_type_num_array_type): Add DW_TAG_rvalue_reference_type to switch.
(get_type_num): Handle DW_TAG_rvalue_reference_type DIEs.
* dwarf2codeview.h (CV_PTR_MODE_RVREF): Define.
---
 gcc/dwarf2codeview.cc | 15 ++-
 gcc/dwarf2codeview.h  |  1 +
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 23175204acd..9f446cb9cbb 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -2293,11 +2293,11 @@ get_type_num_pointer_type (dw_die_ref type, bool 
in_struct)
   return ct->num;
 }
 
-/* Process a DW_TAG_reference_type DIE, add a new LF_POINTER type, and return
-   its number.  */
+/* Process a DW_TAG_reference_type or DW_TAG_rvalue_reference_type DIE, add a
+   new LF_POINTER type, and return its number.  */
 
 static uint32_t
-get_type_num_reference_type (dw_die_ref type, bool in_struct)
+get_type_num_reference_type (dw_die_ref type, bool in_struct, bool rvref)
 {
   uint32_t base_type_num, byte_size;
   dw_die_ref base_type;
@@ -2318,7 +2318,7 @@ get_type_num_reference_type (dw_die_ref type, bool 
in_struct)
   ct->next = NULL;
   ct->kind = LF_POINTER;
   ct->lf_pointer.base_type = base_type_num;
-  ct->lf_pointer.attributes = CV_PTR_MODE_LVREF;
+  ct->lf_pointer.attributes = rvref? CV_PTR_MODE_RVREF : CV_PTR_MODE_LVREF;
 
   if (byte_size == 4)
 ct->lf_pointer.attributes |= CV_PTR_NEAR32;
@@ -3064,6 +3064,7 @@ get_type_num_array_type (dw_die_ref type, bool in_struct)
case DW_TAG_union_type:
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
+   case DW_TAG_rvalue_reference_type:
  size = get_AT_unsigned (t, DW_AT_byte_size);
  break;
 
@@ -3192,7 +3193,11 @@ get_type_num (dw_die_ref type, bool in_struct, bool 
no_fwd_ref)
   break;
 
 case DW_TAG_reference_type:
-  num = get_type_num_reference_type (type, in_struct);
+  num = get_type_num_reference_type (type, in_struct, false);
+  break;
+
+case DW_TAG_rvalue_reference_type:
+  num = get_type_num_reference_type (type, in_struct, true);
   break;
 
 case DW_TAG_const_type:
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index 7d4e3ab1db4..8ede1b29529 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -56,6 +56,7 @@ along with GCC; see the file COPYING3.  If not see
 #define CV_PTR_NEAR32  0x0a
 #define CV_PTR_64  0x0c
 #define CV_PTR_MODE_LVREF  0x20
+#define CV_PTR_MODE_RVREF  0x80
 
 /* LF_MODIFIER values.  */
 #define MOD_const  0x1
-- 
2.44.2



[PATCH 1/2] Output CodeView type information for references

2024-07-20 Thread Mark Harmstone
Translates DW_TAG_reference_type DIEs into LF_POINTER types.

gcc/
* dwarf2codeview.cc (get_type_num_reference_type): New function.
(get_type_num_array_type): Add DW_TAG_reference_type to switch.
(get_type_num): Handle DW_TAG_reference_type DIEs.
* dwarf2codeview.h (CV_PTR_MODE_LVREF): Define.
---
 gcc/dwarf2codeview.cc | 44 +++
 gcc/dwarf2codeview.h  |  1 +
 2 files changed, 45 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index c174f320480..23175204acd 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -2293,6 +2293,45 @@ get_type_num_pointer_type (dw_die_ref type, bool 
in_struct)
   return ct->num;
 }
 
+/* Process a DW_TAG_reference_type DIE, add a new LF_POINTER type, and return
+   its number.  */
+
+static uint32_t
+get_type_num_reference_type (dw_die_ref type, bool in_struct)
+{
+  uint32_t base_type_num, byte_size;
+  dw_die_ref base_type;
+  codeview_custom_type *ct;
+
+  byte_size = get_AT_unsigned (type, DW_AT_byte_size);
+  if (byte_size != 4 && byte_size != 8)
+return 0;
+
+  base_type = get_AT_ref (type, DW_AT_type);
+
+  base_type_num = get_type_num (base_type, in_struct, false);
+  if (base_type_num == 0)
+return 0;
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_POINTER;
+  ct->lf_pointer.base_type = base_type_num;
+  ct->lf_pointer.attributes = CV_PTR_MODE_LVREF;
+
+  if (byte_size == 4)
+ct->lf_pointer.attributes |= CV_PTR_NEAR32;
+  else
+ct->lf_pointer.attributes |= CV_PTR_64;
+
+  ct->lf_pointer.attributes |= byte_size << 13;
+
+  add_custom_type (ct);
+
+  return ct->num;
+}
+
 /* Process a DW_TAG_const_type DIE, adding an LF_MODIFIER type and returning
its number.  */
 
@@ -3024,6 +3063,7 @@ get_type_num_array_type (dw_die_ref type, bool in_struct)
case DW_TAG_class_type:
case DW_TAG_union_type:
case DW_TAG_pointer_type:
+   case DW_TAG_reference_type:
  size = get_AT_unsigned (t, DW_AT_byte_size);
  break;
 
@@ -3151,6 +3191,10 @@ get_type_num (dw_die_ref type, bool in_struct, bool 
no_fwd_ref)
   num = get_type_num_pointer_type (type, in_struct);
   break;
 
+case DW_TAG_reference_type:
+  num = get_type_num_reference_type (type, in_struct);
+  break;
+
 case DW_TAG_const_type:
   num = get_type_num_const_type (type, in_struct);
   break;
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index 8fd3632e524..7d4e3ab1db4 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 /* LF_POINTER attributes.  */
 #define CV_PTR_NEAR32  0x0a
 #define CV_PTR_64  0x0c
+#define CV_PTR_MODE_LVREF  0x20
 
 /* LF_MODIFIER values.  */
 #define MOD_const  0x1
-- 
2.44.2



[PATCH] Fix maybe-uninitialized CodeView LF_INDEX warning

2024-08-12 Thread Mark Harmstone
Initialize last_type to 0 to silence two spurious maybe-uninitialized warnings.
We issue an LF_INDEX continuation subtype for any LF_FIELDLISTs that
overflow, so LF_INDEXes will always have a subtype preceding them (and
thus last_type will always be set).

gcc/
* dwarf2codeview.cc (get_type_num_enumeration_type): Initialize 
last_type
to 0.
(get_type_num_struct): Likewise.
---
 gcc/dwarf2codeview.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index f7107021bc7..7e4faaa9388 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -2448,7 +2448,7 @@ get_type_num_enumeration_type (dw_die_ref type, bool 
in_struct)
   dw_die_ref first_child;
   codeview_custom_type *ct;
   uint16_t count = 0;
-  uint32_t last_type;
+  uint32_t last_type = 0;
 
   if (get_AT_flag (type, DW_AT_declaration))
 return add_enum_forward_def (type);
@@ -2726,7 +2726,7 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   dw_die_ref first_child;
   codeview_custom_type *ct;
   uint16_t num_members = 0;
-  uint32_t last_type;
+  uint32_t last_type = 0;
   const char *name;
 
   if ((in_struct && get_AT_string (type, DW_AT_name))
-- 
2.44.2



Re: [PATCH 00/11] CodeView variables and type system

2024-08-12 Thread Mark Harmstone

Thanks for this - I've just submitted a patch to fix these warnings.

Mark

On 12/08/2024 20:09, Jan-Benedict Glaw wrote:

Building with a really recent GCC on the host for
--target=i686-mingw32crt or --target=i686-cygwin --enable-threads=yes,
I get these new warnings (--enable-werror-always is in place):


[all 2024-08-12 14:21:54] 
/var/lib/laminar/run/gcc-i686-mingw32crt/58/local-toolchain-install/bin/g++  
-fno-PIE -c   -g -O2   -DIN_GCC -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions 
-fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings 
-Wcast-qual -Wmissing-format-attribute -Wconditionally-supported 
-Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros 
-Wno-overlength-strings -Werror  -DHAVE_CONFIG_H -fno-PIE -I. -I. 
-I../../gcc/gcc -I../../gcc/gcc/. -I../../gcc/gcc/../include  
-I../../gcc/gcc/../libcpp/include -I../../gcc/gcc/../libcody  
-I../../gcc/gcc/../libdecnumber -I../../gcc/gcc/../libdecnumber/bid 
-I../libdecnumber -I../../gcc/gcc/../libbacktrace   -o dwarf2codeview.o -MT 
dwarf2codeview.o -MMD -MP -MF ./.deps/dwarf2codeview.TPo 
../../gcc/gcc/dwarf2codeview.cc
[all 2024-08-12 14:22:00] In function 'uint32_t 
get_type_num_enumeration_type(dw_die_ref, bool)',
[all 2024-08-12 14:22:00] inlined from 'uint32_t get_type_num(dw_die_ref, 
bool, bool)' at ../../gcc/gcc/dwarf2codeview.cc:3227:43:
[all 2024-08-12 14:22:00] ../../gcc/gcc/dwarf2codeview.cc:2576:58: error: 
'last_type' may be used uninitialized [-Werror=maybe-uninitialized]
[all 2024-08-12 14:22:00]  2576 | 
ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
[all 2024-08-12 14:22:00]   | 
~^~~
[all 2024-08-12 14:22:00] ../../gcc/gcc/dwarf2codeview.cc: In function 
'uint32_t get_type_num(dw_die_ref, bool, bool)':
[all 2024-08-12 14:22:00] ../../gcc/gcc/dwarf2codeview.cc:2451:12: note: 
'last_type' was declared here
[all 2024-08-12 14:22:00]  2451 |   uint32_t last_type;
[all 2024-08-12 14:22:00]   |^
[all 2024-08-12 14:22:00] In function 'uint32_t get_type_num_struct(dw_die_ref, 
bool, bool*)',
[all 2024-08-12 14:22:00] inlined from 'uint32_t get_type_num(dw_die_ref, 
bool, bool)' at ../../gcc/gcc/dwarf2codeview.cc:3233:33:
[all 2024-08-12 14:22:00] ../../gcc/gcc/dwarf2codeview.cc:2876:60: error: 
'last_type' may be used uninitialized [-Werror=maybe-uninitialized]
[all 2024-08-12 14:22:00]  2876 |   
ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
[all 2024-08-12 14:22:00]   |   
~^~~
[all 2024-08-12 14:22:00] ../../gcc/gcc/dwarf2codeview.cc: In function 
'uint32_t get_type_num(dw_die_ref, bool, bool)':
[all 2024-08-12 14:22:00] ../../gcc/gcc/dwarf2codeview.cc:2729:12: note: 
'last_type' was declared here
[all 2024-08-12 14:22:00]  2729 |   uint32_t last_type;
[all 2024-08-12 14:22:00]   |^
[all 2024-08-12 14:22:00] cc1plus: all warnings being treated as errors
[all 2024-08-12 14:22:00] make[1]: *** [Makefile:1194: dwarf2codeview.o] Error 1
[all 2024-08-12 14:22:00] make[1]: Leaving directory 
'/var/lib/laminar/run/gcc-i686-mingw32crt/58/toolchain-build/gcc'
[all 2024-08-12 14:22:00] make: *** [Makefile:4698: all-gcc] Error 2



[PATCH 3/3] Write CodeView information about stack variables

2024-08-12 Thread Mark Harmstone
Outputs CodeView S_REGREL32 symbols for unoptimized local variables that
are stored on the stack. This includes a change to dwarf2out.cc to make
it easier to extract the function frame base without having to worry
about the function prologue or epilogue.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_REGREL32.
(write_fbreg_variable): New function.
(write_unoptimized_local_variable): Add fblock parameter, and handle
DW_OP_fbreg locations.
(write_unoptimized_function_vars): Add fbloc parameter.
(write_function): Extract frame base from DWARF.
* dwarf2out.cc (convert_cfa_to_fb_loc_list): Output simplified frame
base information for CodeView.
---
 gcc/dwarf2codeview.cc | 105 +++---
 gcc/dwarf2out.cc  |  23 +
 2 files changed, 122 insertions(+), 6 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 4596408f2bb..e01515a0ec4 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -75,6 +75,7 @@ enum cv_sym_type {
   S_REGISTER = 0x1106,
   S_LDATA32 = 0x110c,
   S_GDATA32 = 0x110d,
+  S_REGREL32 = 0x,
   S_COMPILE3 = 0x113c,
   S_LPROC32_ID = 0x1146,
   S_GPROC32_ID = 0x1147,
@@ -2195,12 +2196,94 @@ write_s_register (dw_die_ref die, dw_loc_descr_ref 
loc_ref)
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 }
 
+/* Write an S_REGREL32 symbol in order to represent an unoptimized stack
+   variable.  The memory address is given by a register value plus an offset,
+   so we need to parse the function's DW_AT_frame_base attribute for this.  */
+
+static void
+write_fbreg_variable (dw_die_ref die, dw_loc_descr_ref loc_ref,
+ dw_loc_descr_ref fbloc)
+{
+  unsigned int label_num = ++sym_label_num;
+  const char *name = get_AT_string (die, DW_AT_name);
+  uint32_t type;
+  uint16_t regno;
+  int offset;
+
+  /* This is struct regrel in binutils and REGREL32 in Microsoft's cvinfo.h:
+
+struct regrel
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t offset;
+  uint32_t type;
+  uint16_t reg;
+  char name[];
+} ATTRIBUTE_PACKED;
+  */
+
+  if (!fbloc)
+return;
+
+  if (fbloc->dw_loc_opc >= DW_OP_breg0 && fbloc->dw_loc_opc <= DW_OP_breg31)
+{
+  regno = dwarf_reg_to_cv (fbloc->dw_loc_opc - DW_OP_breg0);
+  offset = fbloc->dw_loc_oprnd1.v.val_int;
+}
+  else if (fbloc->dw_loc_opc == DW_OP_bregx)
+{
+  regno = dwarf_reg_to_cv (fbloc->dw_loc_oprnd1.v.val_int);
+  offset = fbloc->dw_loc_oprnd2.v.val_int;
+}
+  else
+{
+  return;
+}
+
+  if (loc_ref->dw_loc_oprnd1.val_class != dw_val_class_unsigned_const)
+return;
+
+  offset += loc_ref->dw_loc_oprnd1.v.val_int;
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_REGREL32);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, offset);
+  putc ('\n', asm_out_file);
+
+  type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, regno);
+  putc ('\n', asm_out_file);
+
+  ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
 /* Write a symbol representing an unoptimized variable within a function, if
we're able to translate the DIE's DW_AT_location into its CodeView
equivalent.  */
 
 static void
-write_unoptimized_local_variable (dw_die_ref die)
+write_unoptimized_local_variable (dw_die_ref die, dw_loc_descr_ref fbloc)
 {
   dw_attr_node *loc;
   dw_loc_descr_ref loc_ref;
@@ -2258,6 +2341,10 @@ write_unoptimized_local_variable (dw_die_ref die)
   write_s_register (die, loc_ref);
   break;
 
+case DW_OP_fbreg:
+  write_fbreg_variable (die, loc_ref, fbloc);
+  break;
+
 default:
   break;
 }
@@ -2390,7 +2477,7 @@ write_s_end (void)
or blocks that we encounter.  */
 
 static void
-write_unoptimized_function_vars (dw_die_ref die)
+write_unoptimized_function_vars (dw_die_ref die, dw_loc_descr_ref fbloc)
 {
   dw_die_ref first_child, c;
 
@@ -2408,14 +2495,14 @@ write_unoptimized_function_vars (dw_die_ref die)
   {
   case DW_TAG_formal_parameter:
   case DW_TAG_variable:
-   write_unoptimized_local_variable (c);
+   write_unoptimized_local_variable (c, fbloc);
break;
 
   case DW_TAG_lexical_block:
{
   

[PATCH 1/3] Write CodeView information about local static variables

2024-08-12 Thread Mark Harmstone
Outputs CodeView S_LDATA32 symbols, for static variables within
functions, along with S_BLOCK32 and S_END for the beginning and end of
lexical blocks.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_END and S_BLOCK32.
(write_local_s_ldata32): New function.
(write_unoptimized_local_variable): New function.
(write_s_block32): New function.
(write_s_end): New function.
(write_unoptimized_function_vars): New function.
(write_function): Call write_unoptimized_function_vars.
---
 gcc/dwarf2codeview.cc | 258 ++
 1 file changed, 258 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 7e4faaa9388..cb2d64bfcc6 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -70,6 +70,8 @@ along with GCC; see the file COPYING3.  If not see
 /* This is enum SYM_ENUM_e in Microsoft's cvinfo.h.  */
 
 enum cv_sym_type {
+  S_END = 0x0006,
+  S_BLOCK32 = 0x1103,
   S_LDATA32 = 0x110c,
   S_GDATA32 = 0x110d,
   S_COMPILE3 = 0x113c,
@@ -986,6 +988,260 @@ end:
   free (s->data_symbol.name);
 }
 
+/* Write an S_LDATA32 symbol, representing a static variable within a function.
+   This symbol can also appear outside of a function block - see
+   write_data_symbol.  */
+
+static void
+write_local_s_ldata32 (dw_die_ref die, dw_loc_descr_ref loc_ref)
+{
+  unsigned int label_num = ++sym_label_num;
+  const char *name = get_AT_string (die, DW_AT_name);
+  uint32_t type;
+
+  /* This is struct datasym in binutils:
+
+  struct datasym
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint32_t type;
+   uint32_t offset;
+   uint16_t section;
+   char name[];
+  } ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_LDATA32);
+  putc ('\n', asm_out_file);
+
+  type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, type);
+  putc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "\t.secrel32 ");
+  output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
+  fputc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "\t.secidx ");
+  output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
+  fputc ('\n', asm_out_file);
+
+  ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
+/* Write a symbol representing an unoptimized variable within a function, if
+   we're able to translate the DIE's DW_AT_location into its CodeView
+   equivalent.  */
+
+static void
+write_unoptimized_local_variable (dw_die_ref die)
+{
+  dw_attr_node *loc;
+  dw_loc_descr_ref loc_ref;
+
+  loc = get_AT (die, DW_AT_location);
+  if (!loc)
+return;
+
+  if (loc->dw_attr_val.val_class != dw_val_class_loc)
+return;
+
+  loc_ref = loc->dw_attr_val.v.val_loc;
+  if (!loc_ref)
+return;
+
+  switch (loc_ref->dw_loc_opc)
+{
+case DW_OP_addr:
+  write_local_s_ldata32 (die, loc_ref);
+  break;
+
+default:
+  break;
+}
+}
+
+/* Translate a DW_TAG_lexical_block DIE into an S_BLOCK32 symbol, representing
+   a block within an unoptimized function.  Returns false if we're not able
+   to resolve the location, which will prevent the caller from issuing an
+   unneeded S_END.  */
+
+static bool
+write_s_block32 (dw_die_ref die)
+{
+  unsigned int label_num = ++sym_label_num;
+  dw_attr_node *loc_low, *loc_high;
+  const char *label_low, *label_high;
+  rtx rtx_low, rtx_high;
+
+  /* This is struct blocksym in binutils and BLOCKSYM32 in Microsoft's
+ cvinfo.h:
+
+struct blocksym
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t parent;
+  uint32_t end;
+  uint32_t len;
+  uint32_t offset;
+  uint16_t section;
+  char name[];
+} ATTRIBUTE_PACKED;
+  */
+
+  loc_low = get_AT (die, DW_AT_low_pc);
+  if (!loc_low)
+return false;
+
+  if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id)
+return false;
+
+  label_low = loc_low->dw_attr_val.v.val_lbl_id;
+  if (!label_low)
+return false;
+
+  rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low);
+
+  loc_high = get_AT (die, DW_AT_high_pc);
+  if (!loc_high)
+return false;
+
+  if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc)
+return false;
+
+  label_high = loc_high->dw_attr_val.v.val_lbl_id;
+  if (!label_high)
+return false;
+
+  rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  

[PATCH 2/3] Write CodeView information about enregistered variables

2024-08-12 Thread Mark Harmstone
Outputs CodeView S_REGISTER symbols, representing local variables or
parameters that are held in a register.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_REGISTER.
(enum cv_x86_register): New type.
(enum cv_amd64_register): New type.
(dwarf_reg_to_cv): New function.
(write_s_register): New function.
(write_unoptimized_local_variable): Handle parameters and DW_OP_reg*
location types.
---
 gcc/dwarf2codeview.cc | 1188 +
 1 file changed, 1188 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index cb2d64bfcc6..4596408f2bb 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -72,6 +72,7 @@ along with GCC; see the file COPYING3.  If not see
 enum cv_sym_type {
   S_END = 0x0006,
   S_BLOCK32 = 0x1103,
+  S_REGISTER = 0x1106,
   S_LDATA32 = 0x110c,
   S_GDATA32 = 0x110d,
   S_COMPILE3 = 0x113c,
@@ -110,6 +111,962 @@ enum cv_leaf_type {
   LF_UQUADWORD = 0x800a
 };
 
+/* These come from enum CV_HREG_e in Microsoft's cvconst.h.  */
+
+enum cv_x86_register {
+  CV_REG_NONE = 0,
+  CV_REG_AL = 1,
+  CV_REG_CL = 2,
+  CV_REG_DL = 3,
+  CV_REG_BL = 4,
+  CV_REG_AH = 5,
+  CV_REG_CH = 6,
+  CV_REG_DH = 7,
+  CV_REG_BH = 8,
+  CV_REG_AX = 9,
+  CV_REG_CX = 10,
+  CV_REG_DX = 11,
+  CV_REG_BX = 12,
+  CV_REG_SP = 13,
+  CV_REG_BP = 14,
+  CV_REG_SI = 15,
+  CV_REG_DI = 16,
+  CV_REG_EAX = 17,
+  CV_REG_ECX = 18,
+  CV_REG_EDX = 19,
+  CV_REG_EBX = 20,
+  CV_REG_ESP = 21,
+  CV_REG_EBP = 22,
+  CV_REG_ESI = 23,
+  CV_REG_EDI = 24,
+  CV_REG_ES = 25,
+  CV_REG_CS = 26,
+  CV_REG_SS = 27,
+  CV_REG_DS = 28,
+  CV_REG_FS = 29,
+  CV_REG_GS = 30,
+  CV_REG_IP = 31,
+  CV_REG_FLAGS = 32,
+  CV_REG_EIP = 33,
+  CV_REG_EFLAGS = 34,
+  CV_REG_TEMP = 40,
+  CV_REG_TEMPH = 41,
+  CV_REG_QUOTE = 42,
+  CV_REG_PCDR3 = 43,
+  CV_REG_PCDR4 = 44,
+  CV_REG_PCDR5 = 45,
+  CV_REG_PCDR6 = 46,
+  CV_REG_PCDR7 = 47,
+  CV_REG_CR0 = 80,
+  CV_REG_CR1 = 81,
+  CV_REG_CR2 = 82,
+  CV_REG_CR3 = 83,
+  CV_REG_CR4 = 84,
+  CV_REG_DR0 = 90,
+  CV_REG_DR1 = 91,
+  CV_REG_DR2 = 92,
+  CV_REG_DR3 = 93,
+  CV_REG_DR4 = 94,
+  CV_REG_DR5 = 95,
+  CV_REG_DR6 = 96,
+  CV_REG_DR7 = 97,
+  CV_REG_GDTR = 110,
+  CV_REG_GDTL = 111,
+  CV_REG_IDTR = 112,
+  CV_REG_IDTL = 113,
+  CV_REG_LDTR = 114,
+  CV_REG_TR = 115,
+  CV_REG_PSEUDO1 = 116,
+  CV_REG_PSEUDO2 = 117,
+  CV_REG_PSEUDO3 = 118,
+  CV_REG_PSEUDO4 = 119,
+  CV_REG_PSEUDO5 = 120,
+  CV_REG_PSEUDO6 = 121,
+  CV_REG_PSEUDO7 = 122,
+  CV_REG_PSEUDO8 = 123,
+  CV_REG_PSEUDO9 = 124,
+  CV_REG_ST0 = 128,
+  CV_REG_ST1 = 129,
+  CV_REG_ST2 = 130,
+  CV_REG_ST3 = 131,
+  CV_REG_ST4 = 132,
+  CV_REG_ST5 = 133,
+  CV_REG_ST6 = 134,
+  CV_REG_ST7 = 135,
+  CV_REG_CTRL = 136,
+  CV_REG_STAT = 137,
+  CV_REG_TAG = 138,
+  CV_REG_FPIP = 139,
+  CV_REG_FPCS = 140,
+  CV_REG_FPDO = 141,
+  CV_REG_FPDS = 142,
+  CV_REG_ISEM = 143,
+  CV_REG_FPEIP = 144,
+  CV_REG_FPEDO = 145,
+  CV_REG_MM0 = 146,
+  CV_REG_MM1 = 147,
+  CV_REG_MM2 = 148,
+  CV_REG_MM3 = 149,
+  CV_REG_MM4 = 150,
+  CV_REG_MM5 = 151,
+  CV_REG_MM6 = 152,
+  CV_REG_MM7 = 153,
+  CV_REG_XMM0 = 154,
+  CV_REG_XMM1 = 155,
+  CV_REG_XMM2 = 156,
+  CV_REG_XMM3 = 157,
+  CV_REG_XMM4 = 158,
+  CV_REG_XMM5 = 159,
+  CV_REG_XMM6 = 160,
+  CV_REG_XMM7 = 161,
+  CV_REG_XMM00 = 162,
+  CV_REG_XMM01 = 163,
+  CV_REG_XMM02 = 164,
+  CV_REG_XMM03 = 165,
+  CV_REG_XMM10 = 166,
+  CV_REG_XMM11 = 167,
+  CV_REG_XMM12 = 168,
+  CV_REG_XMM13 = 169,
+  CV_REG_XMM20 = 170,
+  CV_REG_XMM21 = 171,
+  CV_REG_XMM22 = 172,
+  CV_REG_XMM23 = 173,
+  CV_REG_XMM30 = 174,
+  CV_REG_XMM31 = 175,
+  CV_REG_XMM32 = 176,
+  CV_REG_XMM33 = 177,
+  CV_REG_XMM40 = 178,
+  CV_REG_XMM41 = 179,
+  CV_REG_XMM42 = 180,
+  CV_REG_XMM43 = 181,
+  CV_REG_XMM50 = 182,
+  CV_REG_XMM51 = 183,
+  CV_REG_XMM52 = 184,
+  CV_REG_XMM53 = 185,
+  CV_REG_XMM60 = 186,
+  CV_REG_XMM61 = 187,
+  CV_REG_XMM62 = 188,
+  CV_REG_XMM63 = 189,
+  CV_REG_XMM70 = 190,
+  CV_REG_XMM71 = 191,
+  CV_REG_XMM72 = 192,
+  CV_REG_XMM73 = 193,
+  CV_REG_XMM0L = 194,
+  CV_REG_XMM1L = 195,
+  CV_REG_XMM2L = 196,
+  CV_REG_XMM3L = 197,
+  CV_REG_XMM4L = 198,
+  CV_REG_XMM5L = 199,
+  CV_REG_XMM6L = 200,
+  CV_REG_XMM7L = 201,
+  CV_REG_XMM0H = 202,
+  CV_REG_XMM1H = 203,
+  CV_REG_XMM2H = 204,
+  CV_REG_XMM3H = 205,
+  CV_REG_XMM4H = 206,
+  CV_REG_XMM5H = 207,
+  CV_REG_XMM6H = 208,
+  CV_REG_XMM7H = 209,
+  CV_REG_MXCSR = 211,
+  CV_REG_EDXEAX = 212,
+  CV_REG_EMM0L = 220,
+  CV_REG_EMM1L = 221,
+  CV_REG_EMM2L = 222,
+  CV_REG_EMM3L = 223,
+  CV_REG_EMM4L = 224,
+  CV_REG_EMM5L = 225,
+  CV_REG_EMM6L = 226,
+  CV_REG_EMM7L = 227,
+  CV_REG_EMM0H = 228,
+  CV_REG_EMM1H = 229,
+  CV_REG_EMM2H = 230,
+  CV_REG_EMM3H = 231,
+  CV_REG_EMM4H = 232,
+  CV_REG_EMM5H = 233,
+  CV_REG_EMM6H = 234,
+  CV_REG_EMM7H = 235,
+  CV_REG_MM00 = 236,
+  CV_REG_MM01 = 237,
+  CV_REG_MM10 = 238,
+  CV_REG_MM11 = 239,
+  CV_REG_MM20 = 240,
+  CV_REG_MM21 = 241,
+  CV_REG_MM30 = 242,
+  CV_REG_MM31 = 243,
+  C

Re: [PATCH 1/3] Write CodeView information about local static variables

2024-08-16 Thread Mark Harmstone

Thanks Jeff. No, CodeView is effectively Windows-specific - it relies on PE for 
reporting the PDB filename, and COFF for the .secidx relocation. I might look 
into moving these bits into the config once I get down to plumbing it for 
aarch64-w64-mingw32.

Mark

On 14/08/2024 05:09, Jeff Law wrote:



On 8/12/24 6:24 PM, Mark Harmstone wrote:

Outputs CodeView S_LDATA32 symbols, for static variables within
functions, along with S_BLOCK32 and S_END for the beginning and end of
lexical blocks.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_END and S_BLOCK32.
(write_local_s_ldata32): New function.
(write_unoptimized_local_variable): New function.
(write_s_block32): New function.
(write_s_end): New function.
(write_unoptimized_function_vars): New function.
(write_function): Call write_unoptimized_function_vars.

This series is fine.  I'm not particularly jazzed about how much target 
specific data shows up in patch #2.  It's probably safe to assume the mapping 
of register number of the codeview number doesn't match the dwarf map.  It's 
probably also safe to assume we're not supporting codeview on any targets other 
than x86 and ix86?

jeff




[PATCH 2/4] Write CodeView information about optimized stack variables

2024-08-18 Thread Mark Harmstone
Outputs S_DEFRANGE_REGISTER_REL symbols for optimized local variables that are
on the stack, consisting of the stack register, the offset, and the code range
for which this applies.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_DEFRANGE_REGISTER_REL.
(write_defrange_register_rel): New function.
(write_optimized_local_variable_loc): Add fbloc param, and call
write_defrange_register_rel.
(write_optimized_local_variable): Add fbloc param.
(write_optimized_function_vars): Add fbloc param.
---
 gcc/dwarf2codeview.cc | 128 +++---
 1 file changed, 119 insertions(+), 9 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 15253978968..74bbf6bc1d7 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -79,6 +79,7 @@ enum cv_sym_type {
   S_COMPILE3 = 0x113c,
   S_LOCAL = 0x113e,
   S_DEFRANGE_REGISTER = 0x1141,
+  S_DEFRANGE_REGISTER_REL = 0x1145,
   S_LPROC32_ID = 0x1146,
   S_GPROC32_ID = 0x1147,
   S_PROC_ID_END = 0x114f
@@ -2409,10 +2410,113 @@ write_defrange_register (dw_loc_descr_ref expr, rtx 
range_start, rtx range_end)
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 }
 
+/* Write an S_DEFRANGE_REGISTER_REL symbol, which describes a range for which
+   an S_LOCAL variable is held in memory given by the value of a certain
+   register plus an offset.  */
+
+static void
+write_defrange_register_rel (dw_loc_descr_ref expr, dw_loc_descr_ref fbloc,
+rtx range_start, rtx range_end)
+{
+  unsigned int label_num = ++sym_label_num;
+  uint16_t regno;
+  int offset;
+
+  /* This is defrange_register_rel in binutils and DEFRANGESYMREGISTERREL in
+ Microsoft's cvinfo.h:
+
+  struct lvar_addr_range
+  {
+   uint32_t offset;
+   uint16_t section;
+   uint16_t length;
+  } ATTRIBUTE_PACKED;
+
+  struct lvar_addr_gap {
+   uint16_t offset;
+   uint16_t length;
+  } ATTRIBUTE_PACKED;
+
+  struct defrange_register_rel
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint16_t reg;
+   uint16_t offset_parent;
+   uint32_t offset_register;
+   struct lvar_addr_range range;
+   struct lvar_addr_gap gaps[];
+  } ATTRIBUTE_PACKED;
+*/
+
+  if (!fbloc)
+return;
+
+  if (fbloc->dw_loc_opc >= DW_OP_breg0 && fbloc->dw_loc_opc <= DW_OP_breg31)
+{
+  regno = dwarf_reg_to_cv (fbloc->dw_loc_opc - DW_OP_breg0);
+  offset = fbloc->dw_loc_oprnd1.v.val_int;
+}
+  else if (fbloc->dw_loc_opc == DW_OP_bregx)
+{
+  regno = dwarf_reg_to_cv (fbloc->dw_loc_oprnd1.v.val_int);
+  offset = fbloc->dw_loc_oprnd2.v.val_int;
+}
+  else
+{
+  return;
+}
+
+  if (expr->dw_loc_oprnd1.val_class != dw_val_class_unsigned_const)
+return;
+
+  offset += expr->dw_loc_oprnd1.v.val_int;
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+ "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+ label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_DEFRANGE_REGISTER_REL);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, regno);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, offset);
+  putc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "\t.secrel32 ");
+  output_addr_const (asm_out_file, range_start);
+  fputc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "\t.secidx ");
+  output_addr_const (asm_out_file, range_start);
+  fputc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  output_addr_const (asm_out_file, range_end);
+  fputs (" - ", asm_out_file);
+  output_addr_const (asm_out_file, range_start);
+  putc ('\n', asm_out_file);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
 /* Try to write an S_DEFRANGE_* symbol for the given DWARF location.  */
 
 static void
-write_optimized_local_variable_loc (dw_loc_descr_ref expr, rtx range_start,
+write_optimized_local_variable_loc (dw_loc_descr_ref expr,
+   dw_loc_descr_ref fbloc, rtx range_start,
rtx range_end)
 {
   if (expr->dw_loc_next)
@@ -2462,6 +2566,10 @@ write_optimized_local_variable_loc (dw_loc_descr_ref 
expr, rtx range_start,
   write_defrange_register (expr, range_start, range_end);
   break;
 
+case DW_OP_fbreg:
+  write_defrange_register_rel (expr, fbloc, range_start, range_end);
+  break;
+
 default:
   break;
 }
@@ -2473,7 +2581,8 @@ write_optimized_local_variable_lo

[PATCH 3/4] Write CodeView S_FRAMEPROC symbols

2024-08-18 Thread Mark Harmstone
Write S_FRAMEPROC symbols, which aren't very useful but seem to be necessary
for Microsoft debuggers to function properly. These symbols come after S_LOCAL
symbols for optimized variables, but before S_REGISTER and S_REGREL32 for
unoptimized variables.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_FRAMEPROC.
(write_s_frameproc): New function.
(write_function): Call write_s_frameproc.
---
 gcc/dwarf2codeview.cc | 80 +--
 1 file changed, 78 insertions(+), 2 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 74bbf6bc1d7..88310504cf7 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -71,6 +71,7 @@ along with GCC; see the file COPYING3.  If not see
 
 enum cv_sym_type {
   S_END = 0x0006,
+  S_FRAMEPROC = 0x1012,
   S_BLOCK32 = 0x1103,
   S_REGISTER = 0x1106,
   S_LDATA32 = 0x110c,
@@ -2822,6 +2823,74 @@ write_s_end (void)
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 }
 
+/* Write the S_FRAMEPROC symbol, which is supposed to give information about
+   the function frame.  It doesn't seem to be really used in modern versions of
+   MSVC, which is why we zero-out everything here.  You still need to write it
+   though, otherwise windbg won't necessarily show all the local variables.  */
+
+static void
+write_s_frameproc (void)
+{
+  unsigned int label_num = ++sym_label_num;
+
+  /* This is struct FRAMEPROCSYM in Microsoft's cvinfo.h:
+
+  struct frameprocsym
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint32_t frame_size;
+   uint32_t padding_size;
+   uint32_t padding_offset;
+   uint32_t saved_registers_size;
+   uint32_t exception_handler_offset;
+   uint16_t exception_handler_section;
+   uint32_t flags;
+  } ATTRIBUTE_PACKED;
+   */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_FRAMEPROC);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
 /* Loop through the DIEs in an unoptimized function, writing out any variables
or blocks that we encounter.  */
 
@@ -3070,9 +3139,16 @@ write_function (codeview_symbol *s)
 fbloc = frame_base->dw_attr_val.v.val_loc;
 
   if (flag_var_tracking)
-write_optimized_function_vars (s->function.die, fbloc, rtx_low, rtx_high);
+{
+  write_optimized_function_vars (s->function.die, fbloc, rtx_low,
+rtx_high);
+  write_s_frameproc ();
+}
   else
-write_unoptimized_function_vars (s->function.die, fbloc);
+{
+  write_s_frameproc ();
+  write_unoptimized_function_vars (s->function.die, fbloc);
+}
 
   /* Output the S_PROC_ID_END record.  */
 
-- 
2.44.2



[PATCH 4/4] Write CodeView information about static locals in optimized code

2024-08-18 Thread Mark Harmstone
Write CodeView S_LDATA32 symbols for static locals in optimized code. We have
to handle these separately, as they come after the S_FRAMEPROC, plus you can't
have S_BLOCK32 symbols like you can in unoptimized code.

gcc/
* dwarf2codeview.cc (write_optimized_static_local_vars): New function.
(write_function): Call write_optimized_static_local_vars.
---
 gcc/dwarf2codeview.cc | 57 +++
 1 file changed, 57 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 88310504cf7..e4c67f921cd 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -3007,6 +3007,62 @@ write_optimized_function_vars (dw_die_ref die, 
dw_loc_descr_ref fbloc,
   while (c != first_child);
 }
 
+/* There's no way to mark the range of a static local variable in an optimized
+   function: there's no S_DEFRANGE_* symbol for this, and you can't have
+   S_BLOCK32 symbols.  So instead we have to loop through after the S_FRAMEPROC
+   has been written, and write the S_LDATA32s at the end.  */
+
+static void
+write_optimized_static_local_vars (dw_die_ref die)
+{
+  dw_die_ref first_child, c;
+
+  first_child = dw_get_die_child (die);
+
+  if (!first_child)
+return;
+
+  c = first_child;
+  do
+  {
+c = dw_get_die_sib (c);
+
+switch (dw_get_die_tag (c))
+  {
+  case DW_TAG_variable:
+   {
+ dw_attr_node *loc;
+ dw_loc_descr_ref loc_ref;
+
+ loc = get_AT (c, DW_AT_location);
+ if (!loc)
+   break;
+
+ if (loc->dw_attr_val.val_class != dw_val_class_loc)
+   break;
+
+ loc_ref = loc->dw_attr_val.v.val_loc;
+ if (!loc_ref)
+   break;
+
+ if (loc_ref->dw_loc_opc != DW_OP_addr)
+   break;
+
+ write_local_s_ldata32 (c, loc_ref);
+ break;
+   }
+
+  case DW_TAG_lexical_block:
+   write_optimized_static_local_vars (c);
+   break;
+
+  default:
+   break;
+  }
+  }
+  while (c != first_child);
+}
+
 /* Write an S_GPROC32_ID symbol, representing a global function, or an
S_LPROC32_ID symbol, for a static function.  */
 
@@ -3143,6 +3199,7 @@ write_function (codeview_symbol *s)
   write_optimized_function_vars (s->function.die, fbloc, rtx_low,
 rtx_high);
   write_s_frameproc ();
+  write_optimized_static_local_vars (s->function.die);
 }
   else
 {
-- 
2.44.2



[PATCH 1/4] Write CodeView information about enregistered optimized variables

2024-08-18 Thread Mark Harmstone
Enable variable tracking when outputting CodeView debug information, and make
it so that we issue debug symbols for optimized variables in registers. This
consists of S_LOCAL symbols, which give the name and the type of local
variables, followed by S_DEFRANGE_REGISTER symbols for the register and the
code for which this applies.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_LOCAL and
S_DEFRANGE_REGISTER.
(write_s_local): New function.
(write_defrange_register): New function.
(write_optimized_local_variable_loc): New function.
(write_optimized_local_variable): New function.
(write_optimized_function_vars): New function.
(write_function): Call write_optimized_function_vars if variable
tracking enabled.
* dwarf2out.cc (typedef var_loc_view): Move to dwarf2out.h.
(struct dw_loc_list_struct): Likewise.
* dwarf2out.h (typedef var_loc_view): Move from dwarf2out.cc.
(struct dw_loc_list_struct): Likewise.
* opts.cc (finish_options): Enable variable tracking for CodeView.
---
 gcc/dwarf2codeview.cc | 316 +-
 gcc/dwarf2out.cc  |  37 -
 gcc/dwarf2out.h   |  37 +
 gcc/opts.cc   |   2 +-
 4 files changed, 353 insertions(+), 39 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index e01515a0ec4..15253978968 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -77,6 +77,8 @@ enum cv_sym_type {
   S_GDATA32 = 0x110d,
   S_REGREL32 = 0x,
   S_COMPILE3 = 0x113c,
+  S_LOCAL = 0x113e,
+  S_DEFRANGE_REGISTER = 0x1141,
   S_LPROC32_ID = 0x1146,
   S_GPROC32_ID = 0x1147,
   S_PROC_ID_END = 0x114f
@@ -1946,6 +1948,56 @@ end:
   free (s->data_symbol.name);
 }
 
+/* Write an S_LOCAL symbol, representing an optimized variable.  This is then
+   followed by various S_DEFRANGE_* symbols, which describe how to find the
+   value of a variable and the range for which this is valid.  */
+
+static void
+write_s_local (dw_die_ref die)
+{
+  unsigned int label_num = ++sym_label_num;
+  const char *name = get_AT_string (die, DW_AT_name);
+  uint32_t type;
+
+  /* This is struct LOCALSYM in Microsoft's cvinfo.h:
+
+struct LOCALSYM {
+  uint16_t reclen;
+  uint16_t rectyp;
+  uint32_t typind;
+  uint16_t flags;
+  char name[];
+};
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+ "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+ label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_LOCAL);
+  putc ('\n', asm_out_file);
+
+  type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
+
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
 /* Write an S_LDATA32 symbol, representing a static variable within a function.
This symbol can also appear outside of a function block - see
write_data_symbol.  */
@@ -2278,6 +2330,194 @@ write_fbreg_variable (dw_die_ref die, dw_loc_descr_ref 
loc_ref,
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 }
 
+/* Write an S_DEFRANGE_REGISTER symbol, which describes a range for which an
+   S_LOCAL variable is held in a certain register.  */
+
+static void
+write_defrange_register (dw_loc_descr_ref expr, rtx range_start, rtx range_end)
+{
+  unsigned int label_num = ++sym_label_num;
+  uint16_t regno;
+
+  /* This is defrange_register in binutils and DEFRANGESYMREGISTER in
+ Microsoft's cvinfo.h:
+
+  struct lvar_addr_range
+  {
+   uint32_t offset;
+   uint16_t section;
+   uint16_t length;
+  } ATTRIBUTE_PACKED;
+
+  struct lvar_addr_gap {
+   uint16_t offset;
+   uint16_t length;
+  } ATTRIBUTE_PACKED;
+
+  struct defrange_register
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint16_t reg;
+   uint16_t attributes;
+   struct lvar_addr_range range;
+   struct lvar_addr_gap gaps[];
+  } ATTRIBUTE_PACKED;
+  */
+
+  if (expr->dw_loc_opc == DW_OP_regx)
+regno = dwarf_reg_to_cv (expr->dw_loc_oprnd1.v.val_int);
+  else
+regno = dwarf_reg_to_cv (expr->dw_loc_opc - DW_OP_reg0);
+
+  if (regno == 0)
+return;
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+ "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+ label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_L

[PATCH 1/5] Handle namespaced names for CodeView

2024-08-26 Thread Mark Harmstone
Run all CodeView names through a new function get_name, which chains
together a DIE's DW_AT_name with that of its parent to create a
C++-style name.

gcc/
* dwarf2codeview.cc (get_name): New function.
(add_enum_forward_def): Call get_name.
(get_type_num_enumeration_type): Call get_name.
(add_struct_forward_def): Call get_name.
(get_type_num_struct): Call get_name.
(add_variable): Call get_name.
(add function): Call get_name.
* dwarf2out.cc (get_die_parent): Rename to dw_get_die_parent and make
non-static.
(generate_type_signature): Handle renamed get_die_parent.
* dwarf2out.h (dw_get_die_parent): Add declaration.
---
 gcc/dwarf2codeview.cc | 92 +--
 gcc/dwarf2out.cc  |  6 +--
 gcc/dwarf2out.h   |  1 +
 3 files changed, 83 insertions(+), 16 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index e4c67f921cd..b71c592b70c 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -4511,6 +4511,79 @@ get_type_num_volatile_type (dw_die_ref type, bool 
in_struct)
   return ct->num;
 }
 
+/* Return the name of a DIE, traversing its parents in order to construct a
+   C++-style name if necessary.  */
+static char *
+get_name (dw_die_ref die)
+{
+  dw_die_ref decl = get_AT_ref (die, DW_AT_specification);
+  dw_die_ref parent;
+  const char *name;
+  char *str;
+  size_t len;
+
+  static const char anon[] = "";
+  static const char sep[] = "::";
+
+  if (decl)
+die = decl;
+
+  name = get_AT_string (die, DW_AT_name);
+
+  if (!name)
+return NULL;
+
+  parent = dw_get_die_parent (die);
+
+  if (!parent || dw_get_die_tag (parent) == DW_TAG_compile_unit)
+return xstrdup (name);
+
+  len = strlen (name);
+  while (parent && dw_get_die_tag (parent) != DW_TAG_compile_unit)
+{
+  const char *ns_name = get_AT_string (parent, DW_AT_name);
+
+  len += sizeof (sep) - 1;
+
+  if (ns_name)
+   len += strlen (ns_name);
+  else
+   len += sizeof (anon) - 1;
+
+  parent = dw_get_die_parent (parent);
+}
+
+  str = (char *) xmalloc (len + 1);
+  str[len] = 0;
+
+  len -= strlen (name);
+  memcpy (str + len, name, strlen (name));
+
+  parent = dw_get_die_parent (die);
+  while (parent && dw_get_die_tag (parent) != DW_TAG_compile_unit)
+{
+  const char *ns_name = get_AT_string (parent, DW_AT_name);
+
+  len -= sizeof (sep) - 1;
+  memcpy (str + len, sep, sizeof (sep) - 1);
+
+  if (ns_name)
+   {
+ len -= strlen (ns_name);
+ memcpy (str + len, ns_name, strlen (ns_name));
+   }
+  else
+   {
+ len -= sizeof (anon) - 1;
+ memcpy (str + len, anon, sizeof (anon) - 1);
+   }
+
+  parent = dw_get_die_parent (parent);
+}
+
+  return str;
+}
+
 /* Add a forward declaration for an enum.  This is legal from C++11 onwards.  
*/
 
 static uint32_t
@@ -4528,7 +4601,7 @@ add_enum_forward_def (dw_die_ref type)
   ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type),
  false, false);
   ct->lf_enum.fieldlist = 0;
-  ct->lf_enum.name = xstrdup (get_AT_string (type, DW_AT_name));
+  ct->lf_enum.name = get_name (type);
 
   add_custom_type (ct);
 
@@ -4688,7 +4761,7 @@ get_type_num_enumeration_type (dw_die_ref type, bool 
in_struct)
   ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type),
  in_struct, false);
   ct->lf_enum.fieldlist = last_type;
-  ct->lf_enum.name = xstrdup (get_AT_string (type, DW_AT_name));
+  ct->lf_enum.name = get_name (type);
 
   add_custom_type (ct);
 
@@ -4775,7 +4848,7 @@ add_struct_forward_def (dw_die_ref type)
   ct->lf_structure.vshape = 0;
   ct->lf_structure.length.neg = false;
   ct->lf_structure.length.num = 0;
-  ct->lf_structure.name = xstrdup (get_AT_string (type, DW_AT_name));
+  ct->lf_structure.name = get_name (type);
 
   add_custom_type (ct);
 
@@ -4823,7 +4896,6 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   codeview_custom_type *ct;
   uint16_t num_members = 0;
   uint32_t last_type = 0;
-  const char *name;
 
   if ((in_struct && get_AT_string (type, DW_AT_name))
   || get_AT_flag (type, DW_AT_declaration))
@@ -5010,13 +5082,7 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   ct->lf_structure.vshape = 0;
   ct->lf_structure.length.neg = false;
   ct->lf_structure.length.num = get_AT_unsigned (type, DW_AT_byte_size);
-
-  name = get_AT_string (type, DW_AT_name);
-
-  if (name)
-ct->lf_structure.name = xstrdup (name);
-  else
-ct->lf_structure.name = NULL;
+  ct->lf_structure.name = get_name (type);
 
   add_custom_type (ct);
 
@@ -5384,7 +5450,7 @@ add_variable (dw_die_ref die)
   s->kind = get_AT (die, DW_AT_external) ? S_GDATA32 : S_LDATA32;
   s->data_symbol.type = get_type_num (get_AT_ref (die, DW_AT_type), false,

[PATCH 5/5] Write LF_MFUNC_ID types for CodeView struct member functions

2024-08-26 Thread Mark Harmstone
If recording the definition of a struct member function, write an
LF_MFUNC_ID type rather than an LF_FUNC_ID. This links directly to the
struct type, rather than to an LF_STRING_ID with its name.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Add LF_MFUNC_ID.
(write_lf_mfunc_id): New function.
(add_lf_func_id): New function.
(add_lf_mfunc_id): New function.
(add_function): Call add_lf_func_id or add_lf_mfunc_id.
---
 gcc/dwarf2codeview.cc | 150 ++
 1 file changed, 137 insertions(+), 13 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 1987575985a..6142431655b 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -112,6 +112,7 @@ enum cv_leaf_type {
   LF_METHOD = 0x150f,
   LF_ONEMETHOD = 0x1511,
   LF_FUNC_ID = 0x1601,
+  LF_MFUNC_ID = 0x1602,
   LF_STRING_ID = 0x1605,
   LF_CHAR = 0x8000,
   LF_SHORT = 0x8001,
@@ -4293,6 +4294,56 @@ write_lf_func_id (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_MFUNC_ID type, which is the version of LF_FUNC_ID for struct
+   functions.  Instead of an LF_STRING_ID for the parent scope, we write the
+   type number of the parent struct.  */
+
+static void
+write_lf_mfunc_id (codeview_custom_type *t)
+{
+  size_t name_len;
+
+  /* This is lf_mfunc_id in binutils and lfMFuncId in Microsoft's cvinfo.h:
+
+struct lf_mfunc_id
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t parent_type;
+  uint32_t function_type;
+  char name[];
+} ATTRIBUTE_PACKED
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_mfunc_id.parent_type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_mfunc_id.function_type);
+  putc ('\n', asm_out_file);
+
+  name_len = strlen (t->lf_mfunc_id.name) + 1;
+
+  ASM_OUTPUT_ASCII (asm_out_file, t->lf_mfunc_id.name, name_len);
+
+  write_cv_padding (4 - (name_len % 4));
+
+  free (t->lf_mfunc_id.name);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write an LF_STRING_ID type, which provides a deduplicated string that other
types can reference.  */
 
@@ -4523,6 +4574,10 @@ write_custom_types (void)
  write_lf_func_id (custom_types);
  break;
 
+   case LF_MFUNC_ID:
+ write_lf_mfunc_id (custom_types);
+ break;
+
case LF_STRING_ID:
  write_lf_string_id (custom_types);
  break;
@@ -6190,21 +6245,13 @@ get_scope_string_id (dw_die_ref die)
   return ret;
 }
 
-/* Process a DW_TAG_subprogram DIE, and add an S_GPROC32_ID or S_LPROC32_ID
-   symbol for this.  */
+/* Add an LF_FUNC_ID type and return its number (see write_lf_func_id).  */
 
-static void
-add_function (dw_die_ref die)
+static uint32_t
+add_lf_func_id (dw_die_ref die, const char *name)
 {
+  uint32_t function_type, scope_type;
   codeview_custom_type *ct;
-  const char *name = get_AT_string (die, DW_AT_name);
-  uint32_t function_type, func_id_type, scope_type;
-  codeview_symbol *s;
-
-  if (!name)
-return;
-
-  /* Add an LF_FUNC_ID type for this function.  */
 
   function_type = get_type_num_subroutine_type (die, false, 0, 0, 0);
   scope_type = get_scope_string_id (die);
@@ -6219,7 +6266,84 @@ add_function (dw_die_ref die)
 
   add_custom_type (ct);
 
-  func_id_type = ct->num;
+  return ct->num;
+}
+
+/* Add an LF_MFUNC_ID type and return its number (see write_lf_mfunc_id).  */
+
+static uint32_t
+add_lf_mfunc_id (dw_die_ref die, const char *name)
+{
+  uint32_t function_type = 0, parent_type;
+  codeview_custom_type *ct;
+  dw_die_ref spec = get_AT_ref (die, DW_AT_specification);
+
+  parent_type = get_type_num (dw_get_die_parent (spec), false, false);
+
+  if (types_htab)
+{
+  codeview_type **slot;
+
+  slot = types_htab->find_slot_with_hash (spec, htab_hash_pointer (spec),
+ NO_INSERT);
+
+  if (slot && *slot)
+   function_type = (*slot)->num;
+}
+
+  if (function_type == 0)
+{
+  function_type = get_type_num_subroutine_type (die, false, parent_type,
+   0, 0);
+}
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_MFUNC_ID;
+  ct->lf_mfunc_id.parent_type = parent_type;
+  ct->lf_mfunc_id.function_type = function_type;
+  ct->lf_mfunc_id.name = xstrdup (name);
+
+  add_custom_type (ct);
+
+  return ct->num;
+}
+
+/* Process a DW_TAG_subprogram DIE, and

[PATCH 2/5] Handle scoping in CodeView LF_FUNC_ID types

2024-08-26 Thread Mark Harmstone
If a function is in a namespace, create an LF_STRING_ID type for the
name of its parent, and record this in the LF_FUNC_ID type we create
for the function.

gcc/
* dwarf2codeview.cc (enum cf_leaf_type): Add LF_STRING_ID.
(struct codeview_custom_type): Add lf_string_id to union.
(struct string_id_hasher): New type.
(string_id_htab): New global variable.
(write_lf_string_id): New function.
(write_custom_types): Call write_lf_string_id.
(codeview_debug_finish): Free string_id_htab.
(add_string_id): New function.
(get_scope_string_id): New function.
(add_function): Call get_scope_string_id and set scope.
---
 gcc/dwarf2codeview.cc | 139 +-
 1 file changed, 137 insertions(+), 2 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index b71c592b70c..2535777d4cb 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -107,6 +107,7 @@ enum cv_leaf_type {
   LF_ENUM = 0x1507,
   LF_MEMBER = 0x150d,
   LF_FUNC_ID = 0x1601,
+  LF_STRING_ID = 0x1605,
   LF_CHAR = 0x8000,
   LF_SHORT = 0x8001,
   LF_USHORT = 0x8002,
@@ -1293,6 +1294,11 @@ struct codeview_custom_type
   uint32_t function_type;
   char *name;
 } lf_func_id;
+struct
+{
+  uint32_t substring;
+  char *string;
+} lf_string_id;
   };
 };
 
@@ -1302,6 +1308,21 @@ struct codeview_deferred_type
   dw_die_ref type;
 };
 
+struct string_id_hasher : nofree_ptr_hash 
+{
+  typedef const char *compare_type;
+
+  static hashval_t hash (const codeview_custom_type *x)
+  {
+return htab_hash_string (x->lf_string_id.string);
+  }
+
+  static bool equal (const codeview_custom_type *x, const char *y)
+  {
+return !strcmp (x->lf_string_id.string, y);
+  }
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -1317,6 +1338,7 @@ static codeview_symbol *sym, *last_sym;
 static hash_table *types_htab;
 static codeview_custom_type *custom_types, *last_custom_type;
 static codeview_deferred_type *deferred_types, *last_deferred_type;
+static hash_table *string_id_htab;
 
 static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool 
no_fwd_ref);
 
@@ -4089,6 +4111,50 @@ write_lf_func_id (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_STRING_ID type, which provides a deduplicated string that other
+   types can reference.  */
+
+static void
+write_lf_string_id (codeview_custom_type *t)
+{
+  size_t string_len;
+
+  /* This is lf_string_id in binutils and lfStringId in Microsoft's cvinfo.h:
+
+struct lf_string_id
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t substring;
+  char string[];
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_string_id.substring);
+  putc ('\n', asm_out_file);
+
+  string_len = strlen (t->lf_string_id.string) + 1;
+
+  ASM_OUTPUT_ASCII (asm_out_file, t->lf_string_id.string, string_len);
+
+  write_cv_padding (4 - (string_len % 4));
+
+  free (t->lf_string_id.string);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
definitions.  */
 
@@ -4152,6 +4218,10 @@ write_custom_types (void)
  write_lf_func_id (custom_types);
  break;
 
+   case LF_STRING_ID:
+ write_lf_string_id (custom_types);
+ break;
+
default:
  break;
}
@@ -4182,6 +4252,9 @@ codeview_debug_finish (void)
 
   if (types_htab)
 delete types_htab;
+
+  if (string_id_htab)
+delete string_id_htab;
 }
 
 /* Translate a DWARF base type (DW_TAG_base_type) into its CodeView
@@ -5461,6 +5534,67 @@ add_variable (dw_die_ref die)
   last_sym = s;
 }
 
+/* Return the type number of the LF_STRING_ID entry corresponding to the given
+   string, creating a new one if necessary.  */
+
+static uint32_t
+add_string_id (const char *s)
+{
+  codeview_custom_type **slot;
+  codeview_custom_type *ct;
+
+  if (!string_id_htab)
+string_id_htab = new hash_table (10);
+
+  slot = string_id_htab->find_slot_with_hash (s, htab_hash_string (s),
+ INSERT);
+  if (*slot)
+return (*slot)->num;
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_STRING_ID;
+  ct->lf_string_id.substring = 0;
+  ct->lf_string_id.string = xstrdup (s);
+
+  add_custom_type (ct);
+
+  *slot = ct;
+
+  return ct->num;
+}
+

[PATCH 3/5] Record static data members in CodeView structs

2024-08-26 Thread Mark Harmstone
Record LF_STMEMBER field list subtypes to represent static data members
in structs.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Add LF_STMEMBER.
(struct codeview_subtype): Add lf_static_member to union.
(write_lf_fieldlist): Handle LF_STMEMBER.
(add_struct_member): New function.
(add_struct_static_member): New function.
(get_accessibility): New function.
(get_type_num_struct): Split out into add_struct_member and
get_accessibility, and handle static members.
---
 gcc/dwarf2codeview.cc | 183 +++---
 1 file changed, 135 insertions(+), 48 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 2535777d4cb..610f884d73d 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -106,6 +106,7 @@ enum cv_leaf_type {
   LF_UNION = 0x1506,
   LF_ENUM = 0x1507,
   LF_MEMBER = 0x150d,
+  LF_STMEMBER = 0x150e,
   LF_FUNC_ID = 0x1601,
   LF_STRING_ID = 0x1605,
   LF_CHAR = 0x8000,
@@ -1218,6 +1219,12 @@ struct codeview_subtype
   codeview_integer offset;
   char *name;
 } lf_member;
+struct
+{
+  uint16_t attributes;
+  uint32_t type;
+  char *name;
+} lf_static_member;
   };
 };
 
@@ -3663,6 +3670,40 @@ write_lf_fieldlist (codeview_custom_type *t)
 
  break;
 
+   case LF_STMEMBER:
+ /* This is lf_static_member in binutils and lfSTMember in Microsoft's
+cvinfo.h:
+
+   struct lf_static_member
+   {
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t type;
+ char name[];
+   } ATTRIBUTE_PACKED;
+ */
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_STMEMBER);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_static_member.attributes);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (4, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_static_member.type);
+ putc ('\n', asm_out_file);
+
+ name_len = strlen (v->lf_static_member.name) + 1;
+ ASM_OUTPUT_ASCII (asm_out_file, v->lf_static_member.name, name_len);
+
+ leaf_len = 8 + name_len;
+ write_cv_padding (4 - (leaf_len % 4));
+
+ free (v->lf_static_member.name);
+ break;
+
default:
  break;
}
@@ -4958,6 +4999,91 @@ create_bitfield (dw_die_ref c)
   return ct->num;
 }
 
+/* Create an LF_MEMBER field list subtype for a struct member, returning its
+   pointer in el and its size in el_len.  */
+
+static void
+add_struct_member (dw_die_ref c, uint16_t accessibility,
+  codeview_subtype **el, size_t *el_len)
+{
+  *el = (codeview_subtype *) xmalloc (sizeof (**el));
+  (*el)->next = NULL;
+  (*el)->kind = LF_MEMBER;
+  (*el)->lf_member.attributes = accessibility;
+
+  if (get_AT (c, DW_AT_data_bit_offset))
+(*el)->lf_member.type = create_bitfield (c);
+  else
+(*el)->lf_member.type = get_type_num (get_AT_ref (c, DW_AT_type),
+ true, false);
+
+  (*el)->lf_member.offset.neg = false;
+  (*el)->lf_member.offset.num = get_AT_unsigned (c, 
DW_AT_data_member_location);
+
+  *el_len = 11 + cv_integer_len (&(*el)->lf_member.offset);
+
+  if (get_AT_string (c, DW_AT_name))
+{
+  (*el)->lf_member.name = xstrdup (get_AT_string (c, DW_AT_name));
+  *el_len += strlen ((*el)->lf_member.name);
+}
+  else
+{
+  (*el)->lf_member.name = NULL;
+}
+
+  if (*el_len % 4)
+*el_len += 4 - (*el_len % 4);
+}
+
+/* Create an LF_STMEMBER field list subtype for a static struct member,
+   returning its pointer in el and its size in el_len.  */
+
+static void
+add_struct_static_member (dw_die_ref c, uint16_t accessibility,
+ codeview_subtype **el, size_t *el_len)
+{
+  *el = (codeview_subtype *) xmalloc (sizeof (**el));
+  (*el)->next = NULL;
+  (*el)->kind = LF_STMEMBER;
+  (*el)->lf_static_member.attributes = accessibility;
+  (*el)->lf_static_member.type = get_type_num (get_AT_ref (c, DW_AT_type),
+  true, false);
+  (*el)->lf_static_member.name = xstrdup (get_AT_string (c, DW_AT_name));
+
+  *el_len = 9 + strlen ((*el)->lf_static_member.name);
+
+  if (*el_len % 4)
+*el_len += 4 - (*el_len % 4);
+}
+
+/* Translate a DWARF DW_AT_accessibility constant into its CodeView
+   equivalent.  If implicit, follow the C++ rules.  */
+
+static uint16_t
+get_accessibility (dw_die_ref c)
+{
+  switch (get_AT_unsigned (c, DW_AT_accessibility))
+{
+case DW_ACCESS_private:
+  return CV_ACCESS_PRIVATE;
+
+case DW_ACCESS_protected:
+  return CV_ACCESS_PROTECTED;
+
+case DW_ACCESS_public:
+  return CV_ACCESS_PUBLIC;
+
+/* Members in a C++ struct or union are public by default, members
+  i

[PATCH 4/5] Record member functions in CodeView struct definitions

2024-08-26 Thread Mark Harmstone
CodeView has two ways of recording struct member functions.
Non-overloaded functions have an LF_ONEMETHOD sub-type in the field
list, which records the name and the function type (LF_MFUNCTION).
Overloaded functions have an LF_METHOD instead, which points to an
LF_METHODLIST, which is an array of links to various LF_MFUNCTION types.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Add LF_MFUNCTION,
LF_METHODLIST, LF_METHOD, and LF_ONEMETHOD.
(struct codeview_subtype): Add lf_onemethod and lf_method to union.
(struct lf_methodlist_entry): New type.
(struct codeview_custom_type): Add lf_mfunc_id, lf_mfunction, and
lf_methodlist to union.
(struct codeview_method): New type.
(struct method_hasher): New type.
(get_type_num_subroutine_type): Add forward declaration.
(write_lf_fieldlist): Handle LF_ONEMETHOD and LF_METHOD.
(write_lf_mfunction): New function.
(write_lf_methodlist): New function.
(write_custom_types): Handle LF_MFUNCTION and LF_METHODLIST.
(add_struct_function): New function.
(get_mfunction_type): New function.
(is_templated_func): New function.
(get_type_num_struct): Handle DW_TAG_subprogram child DIEs.
(get_type_num_subroutine_type): Add containing_class_type, this_type,
and this_adjustment params, and handle creating LF_MFUNCTION types as
well as LF_PROCEDURE.
(get_type_num): New params for get_type_num_subroutine_type.
(add_function): New params for get_type_num_subroutine_type.
* dwarf2codeview.h (CV_METHOD_VANILLA, CV_METHOD_VIRTUAL): Define.
(CV_METHOD_STATIC, CV_METHOD_FRIEND, CV_METHOD_INTRO): Likewise.
(CV_METHOD_PUREVIRT, CV_METHOD_PUREINTRO): Likewise.
---
 gcc/dwarf2codeview.cc | 530 +-
 gcc/dwarf2codeview.h  |   9 +
 2 files changed, 528 insertions(+), 11 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 610f884d73d..1987575985a 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -95,9 +95,11 @@ enum cv_leaf_type {
   LF_MODIFIER = 0x1001,
   LF_POINTER = 0x1002,
   LF_PROCEDURE = 0x1008,
+  LF_MFUNCTION = 0x1009,
   LF_ARGLIST = 0x1201,
   LF_FIELDLIST = 0x1203,
   LF_BITFIELD = 0x1205,
+  LF_METHODLIST = 0x1206,
   LF_INDEX = 0x1404,
   LF_ENUMERATE = 0x1502,
   LF_ARRAY = 0x1503,
@@ -107,6 +109,8 @@ enum cv_leaf_type {
   LF_ENUM = 0x1507,
   LF_MEMBER = 0x150d,
   LF_STMEMBER = 0x150e,
+  LF_METHOD = 0x150f,
+  LF_ONEMETHOD = 0x1511,
   LF_FUNC_ID = 0x1601,
   LF_STRING_ID = 0x1605,
   LF_CHAR = 0x8000,
@@ -1225,9 +1229,27 @@ struct codeview_subtype
   uint32_t type;
   char *name;
 } lf_static_member;
+struct
+{
+  uint16_t method_attribute;
+  uint32_t method_type;
+  char *name;
+} lf_onemethod;
+struct
+{
+  uint16_t count;
+  uint32_t method_list;
+  char *name;
+} lf_method;
   };
 };
 
+struct lf_methodlist_entry
+{
+  uint16_t method_attribute;
+  uint32_t method_type;
+};
+
 struct codeview_custom_type
 {
   struct codeview_custom_type *next;
@@ -1302,10 +1324,32 @@ struct codeview_custom_type
   char *name;
 } lf_func_id;
 struct
+{
+  uint32_t parent_type;
+  uint32_t function_type;
+  char *name;
+} lf_mfunc_id;
+struct
 {
   uint32_t substring;
   char *string;
 } lf_string_id;
+struct
+{
+  uint32_t return_type;
+  uint32_t containing_class_type;
+  uint32_t this_type;
+  uint8_t calling_convention;
+  uint8_t attributes;
+  uint16_t num_parameters;
+  uint32_t arglist;
+  int32_t this_adjustment;
+} lf_mfunction;
+struct
+{
+  unsigned int count;
+  lf_methodlist_entry *entries;
+} lf_methodlist;
   };
 };
 
@@ -1330,6 +1374,31 @@ struct string_id_hasher : nofree_ptr_hash 
   }
 };
 
+struct codeview_method
+{
+  uint16_t attribute;
+  uint32_t type;
+  char *name;
+  unsigned int count;
+  struct codeview_method *next;
+  struct codeview_method *last;
+};
+
+struct method_hasher : nofree_ptr_hash 
+{
+  typedef const char *compare_type;
+
+  static hashval_t hash (const codeview_method *x)
+  {
+return htab_hash_string (x->name);
+  }
+
+  static bool equal (const codeview_method *x, const char *y)
+  {
+return !strcmp (x->name, y);
+  }
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -1348,6 +1417,10 @@ static codeview_deferred_type *deferred_types, 
*last_deferred_type;
 static hash_table *string_id_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,
+ uint32_t containing_class_type,
+ uint32_t this_type,
+

Re: [PATCH 1/5] Handle namespaced names for CodeView

2024-08-28 Thread Mark Harmstone

Thanks Jeff. Yes, sounds good to me.

Mark

On 27/08/2024 16:23, Jeff Law wrote:



On 8/26/24 4:48 PM, Mark Harmstone wrote:

Run all CodeView names through a new function get_name, which chains
together a DIE's DW_AT_name with that of its parent to create a
C++-style name.

gcc/
* dwarf2codeview.cc (get_name): New function.
(add_enum_forward_def): Call get_name.
(get_type_num_enumeration_type): Call get_name.
(add_struct_forward_def): Call get_name.
(get_type_num_struct): Call get_name.
(add_variable): Call get_name.
(add function): Call get_name.
* dwarf2out.cc (get_die_parent): Rename to dw_get_die_parent and make
non-static.
(generate_type_signature): Handle renamed get_die_parent.
* dwarf2out.h (dw_get_die_parent): Add declaration.

This series is fine.

I don't think I'm really adding much with the review step.  You're the expert 
here, so with your permission I'd like to ask the steering committee to ACK you 
as maintainer of the codeview bits.

Jeff





[PATCH 00/11] CodeView variables and type system

2024-06-17 Thread Mark Harmstone
This patch series adds support for outputting global variables when the
-gcodeview option is provided, along with the type system to go along
with this.

As with previous patches, the best way to see the output is run
Microsoft's cvdump.exe against the object file:
https://github.com/microsoft/microsoft-pdb/raw/master/cvdump/cvdump.exe

You'll also need a recentish version of binutils in order to get ld to
output an actual PDB file that can be read by MSVC or windbg.

This ought to be fairly complete as far as C is concerned. Still to come
are functions, local variables, and some C++ things.

Mark Harmstone (11):
  Output CodeView data about variables
  Handle CodeView base types
  Handle typedefs for CodeView
  Handle pointers for CodeView
  Handle const and varible modifiers for CodeView
  Handle enums for CodeView
  Handle structs and classes for CodeView
  Handle unions for CodeView.
  Handle arrays for CodeView
  Handle bitfields for CodeView
  Handle subroutine types in CodeView

 gcc/dwarf2codeview.cc | 2278 -
 gcc/dwarf2codeview.h  |   67 ++
 gcc/dwarf2out.cc  |5 +
 3 files changed, 2341 insertions(+), 9 deletions(-)

-- 
2.44.2



[PATCH 04/11] Handle pointers for CodeView

2024-06-17 Thread Mark Harmstone
Translates DW_TAG_pointer_type DIEs into LF_POINTER symbols, which get
output into the .debug$T section.

gcc/
* dwarf2codeview.cc (FIRST_TYPE): Define.
(struct codeview_custom_type): New structure.
(custom_types, last_custom_type): New variables.
(get_type_num): Prototype.
(write_lf_pointer, write_custom_types): New functions.
(codeview_debug_finish): Call write_custom_types.
(add_custom_type, get_type_num_pointer_type): New functions.
(get_type_num): Handle DW_TAG_pointer_type DIEs.
* dwarf2codeview.h (T_VOID): Define.
(CV_POINTER_32, CV_POINTER_64): Likewise.
(T_32PVOID, T_64PVOID): Likewise.
(CV_PTR_NEAR32, CV_PTR64, LF_POINTER): Likewise.
---
 gcc/dwarf2codeview.cc | 179 +-
 gcc/dwarf2codeview.h  |  13 +++
 2 files changed, 188 insertions(+), 4 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 5006a176260..51401f2d5bc 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -56,6 +56,8 @@ along with GCC; see the file COPYING3.  If not see
 #define CV_CFL_C   0x00
 #define CV_CFL_CXX 0x01
 
+#define FIRST_TYPE 0x1000
+
 #define LINE_LABEL "Lcvline"
 #define END_FUNC_LABEL "Lcvendfunc"
 #define SYMBOL_START_LABEL "Lcvsymstart"
@@ -168,6 +170,22 @@ struct die_hasher : free_ptr_hash 
   }
 };
 
+struct codeview_custom_type
+{
+  struct codeview_custom_type *next;
+  uint32_t num;
+  uint16_t kind;
+
+  union
+  {
+struct
+{
+  uint32_t base_type;
+  uint32_t attributes;
+} lf_pointer;
+  };
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -181,6 +199,9 @@ static const char* last_filename;
 static uint32_t last_file_id;
 static codeview_symbol *sym, *last_sym;
 static hash_table *types_htab;
+static codeview_custom_type *custom_types, *last_custom_type;
+
+static uint32_t get_type_num (dw_die_ref type);
 
 /* Record new line number against the current function.  */
 
@@ -845,6 +866,71 @@ write_codeview_symbols (void)
   asm_fprintf (asm_out_file, "%LLcv_syms_end:\n");
 }
 
+/* Write an LF_POINTER type.  */
+
+static void
+write_lf_pointer (codeview_custom_type *t)
+{
+  /* This is lf_pointer in binutils and lfPointer in Microsoft's cvinfo.h:
+
+struct lf_pointer
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t base_type;
+  uint32_t attributes;
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_pointer.base_type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_pointer.attributes);
+  putc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
+/* Write the .debug$T section, which contains all of our custom type
+   definitions.  */
+
+static void
+write_custom_types (void)
+{
+  targetm.asm_out.named_section (".debug$T", SECTION_DEBUG, NULL);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, CV_SIGNATURE_C13);
+  putc ('\n', asm_out_file);
+
+  while (custom_types)
+{
+  codeview_custom_type *n = custom_types->next;
+
+  switch (custom_types->kind)
+   {
+   case LF_POINTER:
+ write_lf_pointer (custom_types);
+ break;
+   }
+
+  free (custom_types);
+  custom_types = n;
+}
+}
+
 /* Finish CodeView debug info emission.  */
 
 void
@@ -861,6 +947,9 @@ codeview_debug_finish (void)
   write_line_numbers ();
   write_codeview_symbols ();
 
+  if (custom_types)
+write_custom_types ();
+
   if (types_htab)
 delete types_htab;
 }
@@ -993,10 +1082,88 @@ get_type_num_base_type (dw_die_ref type)
 }
 }
 
-/* Process a DIE representing a type definition and return its number.  If
-   it's something we can't handle, return 0.  We keep a hash table so that
-   we're not adding the same type multiple times - though if we do it's not
-   disastrous, as ld will deduplicate everything for us.  */
+/* Add a new codeview_custom_type to our singly-linked custom_types list.  */
+
+static void
+add_custom_type (codeview_custom_type *ct)
+{
+  uint32_t num;
+
+  if (last_custom_type)
+{
+  num = last_custom_type->num + 1;
+  last_custom_type->next = ct;
+}
+  else
+{
+  num = FIRST_TYPE;
+  custom_types = ct;
+}
+
+  last_custom_type = ct;
+
+  ct->num = num;
+}
+
+/* Process a DW_TAG_pointer_type DIE.  If 

[PATCH 01/11] Output CodeView data about variables

2024-06-17 Thread Mark Harmstone
Parse the DW_TAG_variable DIEs, and outputs S_GDATA32 (for global variables)
and S_LDATA32 (static global variables) symbols into the .debug$S section.

gcc/
* dwarf2codeview.cc (S_LDATA32, S_GDATA32): Define.
(struct codeview_symbol): New structure.
(sym, last_sym): New variables.
(write_data_symbol): New function.
(write_codeview_symbols): Call write_data_symbol.
(add_variable, codeview_debug_early_finish): New functions.
* dwarf2codeview.h (codeview_debug_early_finish): Prototype.
* dwarf2out.cc
(dwarf2out_early_finish): Call codeview_debug_early_finish.
---
 gcc/dwarf2codeview.cc | 160 ++
 gcc/dwarf2codeview.h  |   1 +
 gcc/dwarf2out.cc  |   5 ++
 3 files changed, 166 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index db776d79be4..60e84635971 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -46,6 +46,8 @@ along with GCC; see the file COPYING3.  If not see
 
 #define CHKSUM_TYPE_MD51
 
+#define S_LDATA32  0x110c
+#define S_GDATA32  0x110d
 #define S_COMPILE3 0x113c
 
 #define CV_CFL_80386   0x03
@@ -129,6 +131,22 @@ struct codeview_function
   codeview_line_block *blocks, *last_block;
 };
 
+struct codeview_symbol
+{
+  codeview_symbol *next;
+  uint16_t kind;
+
+  union
+  {
+struct
+{
+  uint32_t type;
+  char *name;
+  dw_die_ref die;
+} data_symbol;
+  };
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -140,6 +158,7 @@ static codeview_string *strings, *last_string;
 static codeview_function *funcs, *last_func;
 static const char* last_filename;
 static uint32_t last_file_id;
+static codeview_symbol *sym, *last_sym;
 
 /* Record new line number against the current function.  */
 
@@ -698,6 +717,77 @@ write_compile3_symbol (void)
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 }
 
+/* Write an S_GDATA32 symbol, representing a global variable, or an S_LDATA32
+   symbol, for a static global variable.  */
+
+static void
+write_data_symbol (codeview_symbol *s)
+{
+  unsigned int label_num = ++sym_label_num;
+  dw_attr_node *loc;
+  dw_loc_descr_ref loc_ref;
+
+  /* This is struct datasym in binutils:
+
+  struct datasym
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint32_t type;
+   uint32_t offset;
+   uint16_t section;
+   char name[];
+  } ATTRIBUTE_PACKED;
+  */
+
+  /* Extract the DW_AT_location attribute from the DIE, and make sure it's in
+ in a format we can parse.  */
+
+  loc = get_AT (s->data_symbol.die, DW_AT_location);
+  if (!loc)
+goto end;
+
+  if (loc->dw_attr_val.val_class != dw_val_class_loc)
+goto end;
+
+  loc_ref = loc->dw_attr_val.v.val_loc;
+  if (!loc_ref || loc_ref->dw_loc_opc != DW_OP_addr)
+goto end;
+
+  /* Output the S_GDATA32 / S_LDATA32 record.  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, s->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, s->data_symbol.type);
+  putc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "\t.secrel32 ");
+  output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
+  fputc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "\t.secidx ");
+  output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
+  fputc ('\n', asm_out_file);
+
+  ASM_OUTPUT_ASCII (asm_out_file, s->data_symbol.name,
+   strlen (s->data_symbol.name) + 1);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+
+end:
+  free (s->data_symbol.name);
+}
+
 /* Write the CodeView symbols into the .debug$S section.  */
 
 static void
@@ -714,6 +804,22 @@ write_codeview_symbols (void)
 
   write_compile3_symbol ();
 
+  while (sym)
+{
+  codeview_symbol *n = sym->next;
+
+  switch (sym->kind)
+   {
+   case S_LDATA32:
+   case S_GDATA32:
+ write_data_symbol (sym);
+ break;
+   }
+
+  free (sym);
+  sym = n;
+}
+
   asm_fprintf (asm_out_file, "%LLcv_syms_end:\n");
 }
 
@@ -734,4 +840,58 @@ codeview_debug_finish (void)
   write_codeview_symbols ();
 }
 
+/* Process a DW_TAG_variable DIE, and add an S_GDATA32 or S_LDATA32 symbol for
+   this.  */
+
+static void
+add_variable (dw_die_ref die)
+{
+  codeview_symbol *s;
+  const char *name;
+
+  name = get_AT_string (die, DW_AT_name);
+  if (!name)
+return;
+
+  s = (codeview_symbol *) xm

[PATCH 07/11] Handle structs and classes for CodeView

2024-06-17 Thread Mark Harmstone
Translates DW_TAG_structure_type DIEs into LF_STRUCTURE symbols, and
DW_TAG_class_type DIEs into LF_CLASS symbols.

gcc/
* dwarf2codeview.cc
(struct codeview_type): Add is_fwd_ref member.
(struct codeview_subtype): Add lf_member to union.
(struct codeview_custom_type): Add lf_structure to union.
(struct codeview_deferred_type): New structure.
(deferred_types, last_deferred_type): New variables.
(get_type_num): Add new args to prototype.
(write_lf_fieldlist): Handle LF_MEMBER subtypes.
(write_lf_structure): New function.
(write_custom_types): Call write_lf_structure.
(get_type_num_pointer_type): Add in_struct argument.
(get_type_num_const_type): Likewise.
(get_type_num_volatile_type): Likewise.
(add_enum_forward_def): Fix get_type_num call.
(get_type_num_enumeration_type): Add in-struct argument.
(add_deferred_type, flush_deferred_types): New functions.
(add_struct_forward_def, get_type_num_struct): Likewise.
(get_type_num): Handle self-referential structs.
(add_variable): Fix get_type_num call.
(codeview_debug_early_finish): Call flush_deferred_types.
* dwarf2codeview.h (LF_CLASS, LF_STRUCTURE, LF_MEMBER): Define.
---
 gcc/dwarf2codeview.cc | 513 --
 gcc/dwarf2codeview.h  |   3 +
 2 files changed, 493 insertions(+), 23 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 475a53573e9..9c6614f6297 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -158,6 +158,7 @@ struct codeview_type
 {
   dw_die_ref die;
   uint32_t num;
+  bool is_fwd_ref;
 };
 
 struct die_hasher : free_ptr_hash 
@@ -197,6 +198,13 @@ struct codeview_subtype
 {
   uint32_t type_num;
 } lf_index;
+struct
+{
+  uint16_t attributes;
+  uint32_t type;
+  codeview_integer offset;
+  char *name;
+} lf_member;
   };
 };
 
@@ -232,9 +240,25 @@ struct codeview_custom_type
   uint32_t fieldlist;
   char *name;
 } lf_enum;
+struct
+{
+  uint16_t num_members;
+  uint16_t properties;
+  uint32_t field_list;
+  uint32_t derived_from;
+  uint32_t vshape;
+  codeview_integer length;
+  char *name;
+} lf_structure;
   };
 };
 
+struct codeview_deferred_type
+{
+  struct codeview_deferred_type *next;
+  dw_die_ref type;
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -249,8 +273,9 @@ static uint32_t last_file_id;
 static codeview_symbol *sym, *last_sym;
 static hash_table *types_htab;
 static codeview_custom_type *custom_types, *last_custom_type;
+static codeview_deferred_type *deferred_types, *last_deferred_type;
 
-static uint32_t get_type_num (dw_die_ref type);
+static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool 
no_fwd_ref);
 
 /* Record new line number against the current function.  */
 
@@ -1217,6 +1242,51 @@ write_lf_fieldlist (codeview_custom_type *t)
  free (v->lf_enumerate.name);
  break;
 
+   case LF_MEMBER:
+ /* This is lf_member in binutils and lfMember in Microsoft's
+cvinfo.h:
+
+   struct lf_member
+   {
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t type;
+ uint16_t offset;
+ char name[];
+   } ATTRIBUTE_PACKED;
+ */
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_MEMBER);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_member.attributes);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (4, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_member.type);
+ putc ('\n', asm_out_file);
+
+ leaf_len = 8 + write_cv_integer (&v->lf_member.offset);
+
+ if (v->lf_member.name)
+   {
+ name_len = strlen (v->lf_member.name) + 1;
+ ASM_OUTPUT_ASCII (asm_out_file, v->lf_member.name, name_len);
+   }
+ else
+   {
+ name_len = 1;
+ ASM_OUTPUT_ASCII (asm_out_file, "", name_len);
+   }
+
+ leaf_len += name_len;
+ write_cv_padding (4 - (leaf_len % 4));
+
+ free (v->lf_member.name);
+ break;
+
case LF_INDEX:
  /* This is lf_index in binutils and lfIndex in Microsoft's cvinfo.h:
 
@@ -1308,6 +1378,82 @@ write_lf_enum (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_STRUCTURE or LF_CLASS type (the two have the same structure).  
*/
+
+static void
+write_lf_structure (codeview_custom_type *t)
+{
+  size_t name_len, leaf_len

[PATCH 05/11] Handle const and varible modifiers for CodeView

2024-06-17 Thread Mark Harmstone
Translate DW_TAG_const_type and DW_TAG_volatile_type DIEs into
LF_MODIFIER symbols.

gcc/
* dwarf2codeview.cc
(struct codeview_custom_type): Add lf_modifier to union.
(write_cv_padding, write_lf_modifier): New functions.
(write_custom_types): Call write_lf_modifier.
(get_type_num_const_type): New function.
(get_type_num_volatile_type): Likewise.
(get_type_num): Handle DW_TAG_const_type and
DW_TAG_volatile_type DIEs.
* dwarf2codeview.h (MOD_const, MOD_volatile): Define.
(LF_MODIFIER): Likewise.
---
 gcc/dwarf2codeview.cc | 157 ++
 gcc/dwarf2codeview.h  |   5 ++
 2 files changed, 162 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 51401f2d5bc..05f5f60997e 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -183,6 +183,11 @@ struct codeview_custom_type
   uint32_t base_type;
   uint32_t attributes;
 } lf_pointer;
+struct
+{
+  uint32_t base_type;
+  uint16_t modifier;
+} lf_modifier;
   };
 };
 
@@ -903,6 +908,76 @@ write_lf_pointer (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* All CodeView type definitions have to be aligned to a four-byte boundary,
+   so write some padding bytes if necessary.  These have to be specific values:
+   f3, f2, f1.  */
+
+static void
+write_cv_padding (size_t padding)
+{
+  if (padding == 4 || padding == 0)
+return;
+
+  if (padding == 3)
+{
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, 0xf3);
+  putc ('\n', asm_out_file);
+}
+
+  if (padding >= 2)
+{
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, 0xf2);
+  putc ('\n', asm_out_file);
+}
+
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, 0xf1);
+  putc ('\n', asm_out_file);
+}
+
+/* Write an LF_MODIFIER type, representing a const and/or volatile modification
+   of another type.  */
+
+static void
+write_lf_modifier (codeview_custom_type *t)
+{
+  /* This is lf_modifier in binutils and lfModifier in Microsoft's cvinfo.h:
+
+struct lf_modifier
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t base_type;
+  uint16_t modifier;
+  uint16_t padding;
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_modifier.base_type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_modifier.modifier);
+  putc ('\n', asm_out_file);
+
+  write_cv_padding (2);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
definitions.  */
 
@@ -924,6 +999,10 @@ write_custom_types (void)
case LF_POINTER:
  write_lf_pointer (custom_types);
  break;
+
+   case LF_MODIFIER:
+ write_lf_modifier (custom_types);
+ break;
}
 
   free (custom_types);
@@ -1159,6 +1238,76 @@ get_type_num_pointer_type (dw_die_ref type)
   return ct->num;
 }
 
+/* Process a DW_TAG_const_type DIE, adding an LF_MODIFIER type and returning
+   its number.  */
+
+static uint32_t
+get_type_num_const_type (dw_die_ref type)
+{
+  dw_die_ref base_type;
+  uint32_t base_type_num;
+  codeview_custom_type *ct;
+  bool is_volatile = false;
+
+  base_type = get_AT_ref (type, DW_AT_type);
+  if (!base_type)
+return 0;
+
+  /* Handle case when this is a const volatile type - we only need one
+ LF_MODIFIER for this.  */
+  if (dw_get_die_tag (base_type) == DW_TAG_volatile_type)
+{
+  is_volatile = true;
+
+  base_type = get_AT_ref (base_type, DW_AT_type);
+  if (!base_type)
+   return 0;
+}
+
+  base_type_num = get_type_num (base_type);
+  if (base_type_num == 0)
+return 0;
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_MODIFIER;
+  ct->lf_modifier.base_type = base_type_num;
+  ct->lf_modifier.modifier = MOD_const;
+
+  if (is_volatile)
+ct->lf_modifier.modifier |= MOD_volatile;
+
+  add_custom_type (ct);
+
+  return ct->num;
+}
+
+/* Process a DW_TAG_volatile_type DIE, adding an LF_MODIFIER type and
+   returning its number.  */
+
+static uint32_t
+get_type_num_volatile_type (dw_die_ref type)
+{
+  uint32_t base_type_num;
+  codeview_custom_type *ct;
+
+  base_type_num = get_type_num (get_AT_ref (type, DW_AT_ty

[PATCH 10/11] Handle bitfields for CodeView

2024-06-17 Thread Mark Harmstone
Translates structure members with DW_AT_data_bit_offset set in DWARF
into LF_BITFIELD symbols.

gcc/
* dwarf2codeview.cc
(struct codeview_custom_type): Add lf_bitfield to union.
(write_lf_bitfield): New function.
(write_custom_types): Call write_lf_bitfield.
(create_bitfield): New function.
(get_type_num_struct): Handle bitfields.
* dwarf2codeview.h (LF_BITFIELD): Define.
---
 gcc/dwarf2codeview.cc | 89 ++-
 gcc/dwarf2codeview.h  |  1 +
 2 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 3f1ce5577fc..06267639169 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -256,6 +256,12 @@ struct codeview_custom_type
   uint32_t index_type;
   codeview_integer length_in_bytes;
 } lf_array;
+struct
+{
+  uint32_t base_type;
+  uint8_t length;
+  uint8_t position;
+} lf_bitfield;
   };
 };
 
@@ -1573,6 +1579,50 @@ write_lf_array (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_BITFIELD type.  */
+
+static void
+write_lf_bitfield (codeview_custom_type *t)
+{
+  /* This is lf_bitfield in binutils and lfBitfield in Microsoft's cvinfo.h:
+
+struct lf_bitfield
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t base_type;
+  uint8_t length;
+  uint8_t position;
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_bitfield.base_type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_bitfield.length);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_bitfield.position);
+  putc ('\n', asm_out_file);
+
+  write_cv_padding (2);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
definitions.  */
 
@@ -1619,6 +1669,10 @@ write_custom_types (void)
case LF_ARRAY:
  write_lf_array (custom_types);
  break;
+
+   case LF_BITFIELD:
+ write_lf_bitfield (custom_types);
+ break;
}
 
   free (custom_types);
@@ -2199,6 +2253,33 @@ add_struct_forward_def (dw_die_ref type)
   return ct->num;
 }
 
+/* Add an LF_BITFIELD type, returning its number.  DWARF represents bitfields
+   as members in a struct with a DW_AT_data_bit_offset attribute, whereas in
+   CodeView they're a distinct type.  */
+
+static uint32_t
+create_bitfield (dw_die_ref c)
+{
+  codeview_custom_type *ct;
+  uint32_t base_type;
+
+  base_type = get_type_num (get_AT_ref (c, DW_AT_type), true, false);
+  if (base_type == 0)
+return 0;
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_BITFIELD;
+  ct->lf_bitfield.base_type = base_type;
+  ct->lf_bitfield.length = get_AT_unsigned (c, DW_AT_bit_size);
+  ct->lf_bitfield.position = get_AT_unsigned (c, DW_AT_data_bit_offset);
+
+  add_custom_type (ct);
+
+  return ct->num;
+}
+
 /* Process a DW_TAG_structure_type, DW_TAG_class_type, or DW_TAG_union_type
DIE, add an LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS / LF_UNION type,
and return the number of the latter.  */
@@ -2279,8 +2360,12 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
  break;
}
 
- el->lf_member.type = get_type_num (get_AT_ref (c, DW_AT_type), true,
-   false);
+ if (get_AT (c, DW_AT_data_bit_offset))
+   el->lf_member.type = create_bitfield (c);
+ else
+   el->lf_member.type = get_type_num (get_AT_ref (c, DW_AT_type),
+  true, false);
+
  el->lf_member.offset.neg = false;
  el->lf_member.offset.num = get_AT_unsigned (c,
  
DW_AT_data_member_location);
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index 70eed6bf2aa..70eae554b80 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -64,6 +64,7 @@ along with GCC; see the file COPYING3.  If not see
 #define LF_MODIFIER0x1001
 #define LF_POINTER 0x1002
 #define LF_FIELDLIST   0x1203
+#define LF_BITFIELD0x1205
 #define LF_INDEX   0x1404
 #define LF_ENUMERATE   0x1502
 #define LF_ARRAY   0x1503

[PATCH 03/11] Handle typedefs for CodeView

2024-06-17 Thread Mark Harmstone
gcc/
* dwarf2codeview.cc (get_type_num): Handle typedefs.
---
 gcc/dwarf2codeview.cc | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index eb7c1270e31..5006a176260 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1024,6 +1024,12 @@ get_type_num (dw_die_ref type)
   t->num = get_type_num_base_type (type);
   break;
 
+case DW_TAG_typedef:
+  /* FIXME - signed longs typedef'd as "HRESULT" should get their
+own type (T_HRESULT) */
+  t->num = get_type_num (get_AT_ref (type, DW_AT_type));
+  break;
+
 default:
   t->num = 0;
   break;
-- 
2.44.2



[PATCH 02/11] Handle CodeView base types

2024-06-17 Thread Mark Harmstone
Adds a get_type_num function to translate type DIEs into CodeView
numbers, along with a hash table for this.  For now we just deal with
the base types (integers, Unicode chars, floats, and bools).

gcc/
* dwarf2codeview.cc (struct codeview_type): New structure.
(struct die_hasher): Likewise.
(types_htab): New variable.
(codeview_debug_finish): Free types_htab if allocated.
(get_type_num_base_type, get_type_num): New function.
(add_variable): Call get_type_num.
* dwarf2codeview.h (T_CHAR, T_SHORT, T_LONG, T_QUAD): Define.
(T_UCHAR, T_USHORT, T_ULONG, T_UQUAD, T_BOOL08): Likewise.
(T_REAL32, T_REAL64, T_REAL80, T_REAL128, T_RCHAR): Likewise.
(T_WCHAR, T_INT4, T_UINT4, T_CHAR16, T_CHAR32, T_CHAR8): Likewise.
---
 gcc/dwarf2codeview.cc | 196 +-
 gcc/dwarf2codeview.h  |  23 +
 2 files changed, 218 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 60e84635971..eb7c1270e31 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -147,6 +147,27 @@ struct codeview_symbol
   };
 };
 
+struct codeview_type
+{
+  dw_die_ref die;
+  uint32_t num;
+};
+
+struct die_hasher : free_ptr_hash 
+{
+  typedef dw_die_ref compare_type;
+
+  static hashval_t hash (const codeview_type *x)
+  {
+return htab_hash_pointer (x->die);
+  }
+
+  static bool equal (const codeview_type *x, const dw_die_ref y)
+  {
+return x->die == y;
+  }
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -159,6 +180,7 @@ static codeview_function *funcs, *last_func;
 static const char* last_filename;
 static uint32_t last_file_id;
 static codeview_symbol *sym, *last_sym;
+static hash_table *types_htab;
 
 /* Record new line number against the current function.  */
 
@@ -838,6 +860,178 @@ codeview_debug_finish (void)
   write_source_files ();
   write_line_numbers ();
   write_codeview_symbols ();
+
+  if (types_htab)
+delete types_htab;
+}
+
+/* Translate a DWARF base type (DW_TAG_base_type) into its CodeView
+   equivalent.  */
+
+static uint32_t
+get_type_num_base_type (dw_die_ref type)
+{
+  unsigned int size = get_AT_unsigned (type, DW_AT_byte_size);
+
+  switch (get_AT_unsigned (type, DW_AT_encoding))
+{
+case DW_ATE_signed_char:
+  {
+   const char *name = get_AT_string (type, DW_AT_name);
+
+   if (size != 1)
+ return 0;
+
+   if (name && !strcmp (name, "signed char"))
+ return T_CHAR;
+   else
+ return T_RCHAR;
+  }
+
+case DW_ATE_unsigned_char:
+  if (size != 1)
+   return 0;
+
+  return T_UCHAR;
+
+case DW_ATE_signed:
+  switch (size)
+   {
+   case 2:
+ return T_SHORT;
+
+   case 4:
+ {
+   const char *name = get_AT_string (type, DW_AT_name);
+
+   if (name && !strcmp (name, "int"))
+ return T_INT4;
+   else
+ return T_LONG;
+ }
+
+   case 8:
+ return T_QUAD;
+
+   default:
+ return 0;
+   }
+
+case DW_ATE_unsigned:
+  switch (size)
+   {
+   case 2:
+ {
+   const char *name = get_AT_string (type, DW_AT_name);
+
+   if (name && !strcmp (name, "wchar_t"))
+ return T_WCHAR;
+   else
+ return T_USHORT;
+ }
+
+   case 4:
+ {
+   const char *name = get_AT_string (type, DW_AT_name);
+
+   if (name && !strcmp (name, "unsigned int"))
+ return T_UINT4;
+   else
+ return T_ULONG;
+ }
+
+   case 8:
+ return T_UQUAD;
+
+   default:
+ return 0;
+   }
+
+case DW_ATE_UTF:
+  switch (size)
+   {
+   case 1:
+ return T_CHAR8;
+
+   case 2:
+ return T_CHAR16;
+
+   case 4:
+ return T_CHAR32;
+
+   default:
+ return 0;
+   }
+
+case DW_ATE_float:
+  switch (size)
+   {
+   case 4:
+ return T_REAL32;
+
+   case 8:
+ return T_REAL64;
+
+   case 12:
+ return T_REAL80;
+
+   case 16:
+ return T_REAL128;
+
+   default:
+ return 0;
+   }
+
+case DW_ATE_boolean:
+  if (size == 1)
+   return T_BOOL08;
+  else
+   return 0;
+
+default:
+  return 0;
+}
+}
+
+/* Process a DIE representing a type definition and return its number.  If
+   it's something we can't handle, return 0.  We keep a hash table so that
+   we're not adding the same type multiple times - though if we do it's not
+   disastrous, as ld will deduplicate everything for us.  */
+
+static uint32_t
+get_type_num (dw_die_ref type)
+{
+  codeview_type **slot, *t;
+
+  if (!type)
+return 0;
+
+  if (!types_htab)
+types_htab = new hash_table (10);
+
+  slot = types_htab->find_slot

[PATCH 06/11] Handle enums for CodeView

2024-06-17 Thread Mark Harmstone
Translates DW_TAG_enumeration_type DIEs into LF_ENUM symbols.

gcc/
* dwarf2codeview.cc (MAX_FIELDLIST_SIZE): Define.
(struct codeview_integer): New structure.
(struct codeview_subtype): Likewise
(struct codeview_custom_type): Add lf_fieldlist and lf_enum
to union.
(write_cv_integer, cv_integer_len): New functions.
(write_lf_fieldlist, write_lf_enum): Likewise.
(write_custom_types): Call write_lf_fieldlist and write_lf_enum.
(add_enum_forward_def): New function.
(get_type_num_enumeration_type): Likewise.
(get_type_num): Handle DW_TAG_enumeration_type DIEs.
* dwarf2codeview.h (LF_FIELDLIST, LF_INDEX, LF_ENUMERATE): Define.
(LF_ENUM, LF_CHAR, LF_SHORT, LF_USHORT, LF_LONG): Likewise.
(LF_ULONG, LF_QUADWORD, LF_UQUADWORD): Likewise.
(CV_ACCESS_PRIVATE, CV_ACCESS_PROTECTED): Likewise.
(CV_ACCESS_PUBLIC, CV_PROP_FWDREF): Likewise.
---
 gcc/dwarf2codeview.cc | 524 ++
 gcc/dwarf2codeview.h  |  17 ++
 2 files changed, 541 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 05f5f60997e..475a53573e9 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -63,6 +63,11 @@ along with GCC; see the file COPYING3.  If not see
 #define SYMBOL_START_LABEL "Lcvsymstart"
 #define SYMBOL_END_LABEL   "Lcvsymend"
 
+/* There's two bytes available for each type's size, but follow MSVC's lead in
+   capping the LF_FIELDLIST size at fb00 (minus 8 bytes for the LF_INDEX
+   pointing to the overflow entry).  */
+#define MAX_FIELDLIST_SIZE 0xfaf8
+
 #define HASH_SIZE 16
 
 struct codeview_string
@@ -170,6 +175,31 @@ struct die_hasher : free_ptr_hash 
   }
 };
 
+struct codeview_integer
+{
+  bool neg;
+  uint64_t num;
+};
+
+struct codeview_subtype
+{
+  struct codeview_subtype *next;
+  uint16_t kind;
+
+  union
+  {
+struct
+{
+  char *name;
+  struct codeview_integer value;
+} lf_enumerate;
+struct
+{
+  uint32_t type_num;
+} lf_index;
+  };
+};
+
 struct codeview_custom_type
 {
   struct codeview_custom_type *next;
@@ -188,6 +218,20 @@ struct codeview_custom_type
   uint32_t base_type;
   uint16_t modifier;
 } lf_modifier;
+struct
+{
+  size_t length;
+  codeview_subtype *subtypes;
+  codeview_subtype *last_subtype;
+} lf_fieldlist;
+struct
+{
+  uint16_t count;
+  uint16_t properties;
+  uint32_t underlying_type;
+  uint32_t fieldlist;
+  char *name;
+} lf_enum;
   };
 };
 
@@ -978,6 +1022,292 @@ write_lf_modifier (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write a CodeView extensible integer.  If the value is non-negative and
+   < 0x8000, the value gets written directly as an uint16_t.  Otherwise, we
+   output two bytes for the integer type (LF_CHAR, LF_SHORT, ...), and the
+   actual value follows.  */
+
+static size_t
+write_cv_integer (codeview_integer *i)
+{
+  if (i->neg)
+{
+  if (i->num <= 0x80)
+   {
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_CHAR);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (1, false), asm_out_file);
+ fprint_whex (asm_out_file, -i->num);
+ putc ('\n', asm_out_file);
+
+ return 3;
+   }
+  else if (i->num <= 0x8000)
+   {
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_SHORT);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, -i->num);
+ putc ('\n', asm_out_file);
+
+ return 4;
+   }
+  else if (i->num <= 0x8000)
+   {
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_LONG);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (4, false), asm_out_file);
+ fprint_whex (asm_out_file, -i->num);
+ putc ('\n', asm_out_file);
+
+ return 6;
+   }
+  else
+   {
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_QUADWORD);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (8, false), asm_out_file);
+ fprint_whex (asm_out_file, -i->num);
+ putc ('\n', asm_out_file);
+
+ return 10;
+   }
+}
+  else
+{
+  if (i->num <= 0x7fff)
+   {
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, i->num);
+ putc ('\n', asm_out_file);
+
+ return 2;
+   }
+  else if (i->num <= 0x)
+   {
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_USHORT);
+ putc ('

[PATCH 08/11] Handle unions for CodeView.

2024-06-17 Thread Mark Harmstone
Translates DW_TAG_union_type DIEs into LF_UNION symbols.

gcc/
* dwarf2codeview.cc (write_lf_union): New function.
(write_custom_types): Call write_lf_union.
(add_struct_forward_def): Handle DW_TAG_union_type DIEs.
(get_type_num_struct): Handle unions.
(get_type_num): Handle DW_TAG_union_type DIEs.
* dwarf2codeview.h (LF_UNION): Define.
---
 gcc/dwarf2codeview.cc | 91 ---
 gcc/dwarf2codeview.h  |  1 +
 2 files changed, 86 insertions(+), 6 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 9c6614f6297..9e3b64522b2 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1454,6 +1454,72 @@ write_lf_structure (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_UNION type.  */
+
+static void
+write_lf_union (codeview_custom_type *t)
+{
+  size_t name_len, leaf_len;
+
+  /* This is lf_union in binutils and lfUnion in Microsoft's cvinfo.h:
+
+struct lf_union
+{
+  uint16_t size;
+  uint16_t kind;
+  uint16_t num_members;
+  uint16_t properties;
+  uint32_t field_list;
+  uint16_t length;
+  char name[];
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.num_members);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.properties);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.field_list);
+  putc ('\n', asm_out_file);
+
+  leaf_len = 12 + write_cv_integer (&t->lf_structure.length);
+
+  if (t->lf_structure.name)
+{
+  name_len = strlen (t->lf_structure.name) + 1;
+  ASM_OUTPUT_ASCII (asm_out_file, t->lf_structure.name, name_len);
+}
+  else
+{
+  static const char unnamed_struct[] = "";
+
+  name_len = sizeof (unnamed_struct);
+  ASM_OUTPUT_ASCII (asm_out_file, unnamed_struct, name_len);
+}
+
+  leaf_len += name_len;
+  write_cv_padding (4 - (leaf_len % 4));
+
+  free (t->lf_structure.name);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
definitions.  */
 
@@ -1492,6 +1558,10 @@ write_custom_types (void)
case LF_CLASS:
  write_lf_structure (custom_types);
  break;
+
+   case LF_UNION:
+ write_lf_union (custom_types);
+ break;
}
 
   free (custom_types);
@@ -2026,7 +2096,7 @@ flush_deferred_types (void)
   last_deferred_type = NULL;
 }
 
-/* Add a forward definition for a struct or class.  */
+/* Add a forward definition for a struct, class, or union.  */
 
 static uint32_t
 add_struct_forward_def (dw_die_ref type)
@@ -2047,6 +2117,10 @@ add_struct_forward_def (dw_die_ref type)
   ct->kind = LF_STRUCTURE;
   break;
 
+case DW_TAG_union_type:
+  ct->kind = LF_UNION;
+  break;
+
 default:
   break;
 }
@@ -2068,9 +2142,9 @@ add_struct_forward_def (dw_die_ref type)
   return ct->num;
 }
 
-/* Process a DW_TAG_structure_type or DW_TAG_class_type DIE, add an
-   LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS type, and return the number of
-   the latter.  */
+/* Process a DW_TAG_structure_type, DW_TAG_class_type, or DW_TAG_union_type
+   DIE, add an LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS / LF_UNION type,
+   and return the number of the latter.  */
 
 static uint32_t
 get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref)
@@ -2227,8 +2301,8 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   ct = ct2;
 }
 
-  /* Now add an LF_STRUCTURE / LF_CLASS, pointing to the LF_FIELDLIST we just
- added.  */
+  /* Now add an LF_STRUCTURE / LF_CLASS / LF_UNION, pointing to the
+ LF_FIELDLIST we just added.  */
 
   ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
 
@@ -2244,6 +2318,10 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   ct->kind = LF_STRUCTURE;
   break;
 
+case DW_TAG_union_type:
+  ct->kind = LF_UNION;
+  break;
+
 default:
   break;
 }
@@ -2325,6 +2403,7 @@ get_type_num (dw_die_ref type, bool in_struct, bool 
no_fwd_ref)
 
 case DW_TAG_structure_type:
 case DW_TAG_class_type:
+case DW_TAG_union_type:
   num = get_type_num_struct (type, in_struct, &is_fwd_ref);
   break;
 
diff --git a/gcc/dwarf2codeview.h b/gcc/dwar

[PATCH 09/11] Handle arrays for CodeView

2024-06-17 Thread Mark Harmstone
Translates DW_TAG_array_type DIEs into LF_ARRAY symbols.

gcc/
* dwarf2codeview.cc
(struct codeview_custom_type): Add lf_array to union.
(write_lf_array): New function.
(write_custom_types): Call write_lf_array.
(get_type_num_array_type): New function.
(get_type_num): Handle DW_TAG_array_type DIEs.
* dwarf2codeview.h (LF_ARRAY): Define.
---
 gcc/dwarf2codeview.cc | 179 ++
 gcc/dwarf2codeview.h  |   1 +
 2 files changed, 180 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 9e3b64522b2..3f1ce5577fc 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -250,6 +250,12 @@ struct codeview_custom_type
   codeview_integer length;
   char *name;
 } lf_structure;
+struct
+{
+  uint32_t element_type;
+  uint32_t index_type;
+  codeview_integer length_in_bytes;
+} lf_array;
   };
 };
 
@@ -1520,6 +1526,53 @@ write_lf_union (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_ARRAY type.  */
+
+static void
+write_lf_array (codeview_custom_type *t)
+{
+  size_t leaf_len;
+
+  /* This is lf_array in binutils and lfArray in Microsoft's cvinfo.h:
+
+struct lf_array
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t element_type;
+  uint32_t index_type;
+  uint16_t length_in_bytes;
+  char name[];
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_array.element_type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_array.index_type);
+  putc ('\n', asm_out_file);
+
+  leaf_len = 13 + write_cv_integer (&t->lf_array.length_in_bytes);
+
+  ASM_OUTPUT_ASCII (asm_out_file, "", 1);
+
+  write_cv_padding (4 - (leaf_len % 4));
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
definitions.  */
 
@@ -1562,6 +1615,10 @@ write_custom_types (void)
case LF_UNION:
  write_lf_union (custom_types);
  break;
+
+   case LF_ARRAY:
+ write_lf_array (custom_types);
+ break;
}
 
   free (custom_types);
@@ -2346,6 +2403,124 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   return ct->num;
 }
 
+/* Process a DW_TAG_array_type DIE, adding an LF_ARRAY type and returning its
+   number.  */
+
+static uint32_t
+get_type_num_array_type (dw_die_ref type, bool in_struct)
+{
+  dw_die_ref base_type, t, first_child, c, *dimension_arr;
+  uint64_t size = 0;
+  unsigned int dimensions, i;
+  uint32_t element_type;
+
+  base_type = get_AT_ref (type, DW_AT_type);
+  if (!base_type)
+return 0;
+
+  /* We need to know the size of our base type.  Loop through until we find
+ it.  */
+  t = base_type;
+  while (t && size == 0)
+{
+  switch (dw_get_die_tag (t))
+   {
+   case DW_TAG_const_type:
+   case DW_TAG_volatile_type:
+   case DW_TAG_typedef:
+   case DW_TAG_enumeration_type:
+ t = get_AT_ref (t, DW_AT_type);
+ break;
+
+   case DW_TAG_base_type:
+   case DW_TAG_structure_type:
+   case DW_TAG_class_type:
+   case DW_TAG_union_type:
+   case DW_TAG_pointer_type:
+ size = get_AT_unsigned (t, DW_AT_byte_size);
+ break;
+
+   default:
+ return 0;
+   }
+}
+
+  if (size == 0)
+return 0;
+
+  first_child = dw_get_die_child (type);
+  if (!first_child)
+return 0;
+
+  element_type = get_type_num (base_type, in_struct, false);
+  if (element_type == 0)
+return 0;
+
+  /* Create an array of our DW_TAG_subrange_type children, in reverse order.
+ We have to do this because unlike DWARF CodeView doesn't have
+ multidimensional arrays, so instead we do arrays of arrays.  */
+
+  dimensions = 0;
+  c = first_child;
+  do
+{
+  c = dw_get_die_sib (c);
+  if (dw_get_die_tag (c) != DW_TAG_subrange_type)
+   continue;
+
+  dimensions++;
+}
+  while (c != first_child);
+
+  if (dimensions == 0)
+return 0;
+
+  dimension_arr = (dw_die_ref *) xmalloc (sizeof (dw_die_ref) * dimensions);
+
+  c = first_child;
+  i = 0;
+  do
+{
+  c = dw_get_die_sib (c);
+  if (dw_get_die_tag (c) != DW_TAG_subrange_type)
+   continue;
+
+  dimension_arr[dimensions - i - 1] = c;
+  i++;
+}
+  while (c != first_child);
+
+  /* Record an LF_ARRAY entry for each array dimensi

[PATCH 11/11] Handle subroutine types in CodeView

2024-06-17 Thread Mark Harmstone
Translates DW_TAG_subroutine_type DIEs into LF_PROCEDURE symbols.

gcc/
* dwarf2codeview.cc
(struct codeview_custom_type): Add lf_procedure and lf_arglist
to union.
(write_lf_procedure, write_lf_arglist): New functions.
(write_custom_types): Call write_lf_procedure and
write_lf_arglist.
(get_type_num_subroutine_type): New function.
(get_type_num): Handle DW_TAG_subroutine_type DIEs.
* dwarf2codeview.h (LF_PROCEDURE, LF_ARGLIST): Define.
---
 gcc/dwarf2codeview.cc | 238 ++
 gcc/dwarf2codeview.h  |   2 +
 2 files changed, 240 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 06267639169..e8ed3713480 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -262,6 +262,19 @@ struct codeview_custom_type
   uint8_t length;
   uint8_t position;
 } lf_bitfield;
+struct
+{
+  uint32_t return_type;
+  uint8_t calling_convention;
+  uint8_t attributes;
+  uint16_t num_parameters;
+  uint32_t arglist;
+} lf_procedure;
+struct
+{
+  uint32_t num_entries;
+  uint32_t *args;
+} lf_arglist;
   };
 };
 
@@ -1623,6 +1636,102 @@ write_lf_bitfield (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_PROCEDURE type.  Function pointers are implemented as pointers
+   to one of these.  */
+
+static void
+write_lf_procedure (codeview_custom_type *t)
+{
+  /* This is lf_procedure in binutils and lfProc in Microsoft's cvinfo.h:
+
+struct lf_procedure
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t return_type;
+  uint8_t calling_convention;
+  uint8_t attributes;
+  uint16_t num_parameters;
+  uint32_t arglist;
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_procedure.return_type);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_procedure.calling_convention);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (1, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_procedure.attributes);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_procedure.num_parameters);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_procedure.arglist);
+  putc ('\n', asm_out_file);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
+/* Write an LF_ARGLIST type.  This is just a list of other types.  LF_PROCEDURE
+   entries point to one of these.  */
+
+static void
+write_lf_arglist (codeview_custom_type *t)
+{
+  /* This is lf_arglist in binutils and lfArgList in Microsoft's cvinfo.h:
+
+struct lf_arglist
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t num_entries;
+  uint32_t args[];
+} ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+  t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_arglist.num_entries);
+  putc ('\n', asm_out_file);
+
+  for (uint32_t i = 0; i < t->lf_arglist.num_entries; i++)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_arglist.args[i]);
+  putc ('\n', asm_out_file);
+}
+
+  free (t->lf_arglist.args);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
definitions.  */
 
@@ -1673,6 +1782,14 @@ write_custom_types (void)
case LF_BITFIELD:
  write_lf_bitfield (custom_types);
  break;
+
+   case LF_PROCEDURE:
+ write_lf_procedure (custom_types);
+ break;
+
+   case LF_ARGLIST:
+ write_lf_arglist (custom_types);
+ break;
}
 
   free (custom_types);
@@ -2488,6 +2605,123 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   return ct->num;
 }
 
+/* Process a DW_TAG_subroutine_type DIE, adding an LF_ARGLIST and an
+   LF_PROCEDURE type, and returning the number of the latter.  */
+
+static uint32_

Re: [PATCH 05/11] Handle const and varible modifiers for CodeView

2024-06-24 Thread Mark Harmstone

On 24/6/24 04:39, Jeff Law wrote:



So presumably you're freeing these objects elsewhere?  I see the free 
(custom_types), but I don' see where you free an subobjects.  Did I miss 
something?

I'll go ahead and commit, but please double check for memory leaks.


Thanks Jeff. I just realized I wrote "varible" rather than "volatile" - ah well.

See patch 4 - write_custom_types loops through the custom_types linked list, 
and removes and frees the head until it's empty.

Mark


[PATCH] Fix ICE when using -gcodeview with empty struct

2024-07-28 Thread Mark Harmstone
Empty structs result in empty LF_FIELDLIST types, which are valid, but
we weren't accounting for this and assuming they had to contain
subtypes.

gcc/
* dwarf2codeview.cc (get_type_num_struct): Fix NULL pointer dereference.
---
 gcc/dwarf2codeview.cc | 7 +--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index b16c6960f63..470cbae7110 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -2858,8 +2858,11 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
   ct2 = ct->next;
   ct->next = NULL;
 
-  if (ct->lf_fieldlist.last_subtype->kind == LF_INDEX)
-   ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
+  if (ct->lf_fieldlist.last_subtype
+ && ct->lf_fieldlist.last_subtype->kind == LF_INDEX)
+   {
+ ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
+   }
 
   add_custom_type (ct);
   last_type = ct->num;
-- 
2.44.2



[PATCH] Fix handling of const or volatile void pointers in CodeView

2024-08-04 Thread Mark Harmstone
DWARF represents voids in DW_TAG_const_type and DW_TAG_volatile_type
DIEs by the absence of a DW_AT_type attribute, which we weren't handling
correctly.

gcc/
* dwarf2codeview.cc (get_type_num_const_type): Handle missing
DW_AT_type attribute.
(get_type_num_volatile_type): Likewise.
---
 gcc/dwarf2codeview.cc | 36 
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 470cbae7110..f7107021bc7 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -2344,23 +2344,26 @@ get_type_num_const_type (dw_die_ref type, bool 
in_struct)
   bool is_volatile = false;
 
   base_type = get_AT_ref (type, DW_AT_type);
-  if (!base_type)
-return 0;
 
   /* Handle case when this is a const volatile type - we only need one
  LF_MODIFIER for this.  */
-  if (dw_get_die_tag (base_type) == DW_TAG_volatile_type)
+  if (base_type && dw_get_die_tag (base_type) == DW_TAG_volatile_type)
 {
   is_volatile = true;
 
   base_type = get_AT_ref (base_type, DW_AT_type);
-  if (!base_type)
-   return 0;
 }
 
-  base_type_num = get_type_num (base_type, in_struct, false);
-  if (base_type_num == 0)
-return 0;
+  if (!base_type)
+{
+  base_type_num = T_VOID;
+}
+  else
+{
+  base_type_num = get_type_num (base_type, in_struct, false);
+  if (base_type_num == 0)
+   return 0;
+}
 
   ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
 
@@ -2383,13 +2386,22 @@ get_type_num_const_type (dw_die_ref type, bool 
in_struct)
 static uint32_t
 get_type_num_volatile_type (dw_die_ref type, bool in_struct)
 {
+  dw_die_ref base_type;
   uint32_t base_type_num;
   codeview_custom_type *ct;
 
-  base_type_num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct,
-   false);
-  if (base_type_num == 0)
-return 0;
+  base_type = get_AT_ref (type, DW_AT_type);
+
+  if (base_type)
+{
+  base_type_num = get_type_num (base_type, in_struct, false);
+  if (base_type_num == 0)
+   return 0;
+}
+  else
+{
+  base_type_num = T_VOID;
+}
 
   ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
 
-- 
2.44.2



[PATCH 01/24] Add -gcodeview debugging option

2021-03-20 Thread Mark Harmstone
ooks;
 extern const struct gcc_debug_hooks vmsdbg_debug_hooks;
+extern const struct gcc_debug_hooks pdb_debug_hooks;
 
 /* Dwarf2 frame information.  */
 
diff --git a/gcc/defaults.h b/gcc/defaults.h
index f1a38626624..c6bbb203ef3 100644
--- a/gcc/defaults.h
+++ b/gcc/defaults.h
@@ -922,6 +922,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  
If not, see
 #elif defined XCOFF_DEBUGGING_INFO
 #define PREFERRED_DEBUGGING_TYPE XCOFF_DEBUG
 
+#elif defined PDB_DEBUGGING_INFO
+#define PREFERRED_DEBUGGING_TYPE PDB_DEBUG
+
 #else
 /* No debugging format is supported by this target.  */
 #define PREFERRED_DEBUGGING_TYPE NO_DEBUG
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 527d362533a..f83cec6f5e0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -435,6 +435,7 @@ Objective-C and Objective-C++ Dialects}.
 -gstabs  -gstabs+  -gstrict-dwarf  -gno-strict-dwarf @gol
 -gas-loc-support  -gno-as-loc-support @gol
 -gas-locview-support  -gno-as-locview-support @gol
+-gcodeview @gol
 -gcolumn-info  -gno-column-info @gol
 -gstatement-frontiers  -gno-statement-frontiers @gol
 -gvariable-location-views  -gno-variable-location-views @gol
@@ -8707,6 +8708,12 @@ assembler (GAS) to fail with an error.
 Produce debugging information in Alpha/VMS debug format (if that is
 supported).  This is the format used by DEBUG on Alpha/VMS systems.
 
+@item -gcodeview
+@opindex gcodeview
+Produce debugging information in CodeView debug format (if that is
+supported).  This is the format used by Microsoft Visual C++ on
+Windows.
+
 @item -g@var{level}
 @itemx -ggdb@var{level}
 @itemx -gstabs@var{level}
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index b092c563f3d..d4d7bdd5158 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -27,8 +27,9 @@ enum debug_info_type
   DWARF2_DEBUG,/* Write Dwarf v2 debug info (using dwarf2out.c).  
*/
   XCOFF_DEBUG, /* Write IBM/Xcoff debug info (using dbxout.c).  */
   VMS_DEBUG,/* Write VMS debug info (using vmsdbgout.c).  */
-  VMS_AND_DWARF2_DEBUG /* Write VMS debug info (using vmsdbgout.c).
-  and DWARF v2 debug info (using dwarf2out.c).  */
+  VMS_AND_DWARF2_DEBUG, /* Write VMS debug info (using vmsdbgout.c).
+  and DWARF v2 debug info (using dwarf2out.c).  */
+  PDB_DEBUG/* Write CodeView debug info (using pdbout.c).  */
 };
 
 enum debug_info_levels
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 9f790db0daf..327cb4387db 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -4289,6 +4289,12 @@ driver_handle_option (struct gcc_options *opts,
   handle_foffload_option (arg);
   break;
 
+case OPT_gcodeview:
+  /* If we've generated CodeView debugging information, make sure
+   * linker creates a PDB file for it. */
+  add_infile ("--pdb=", "*");
+  break;
+
 default:
   /* Various driver options need no special processing at this
 point, having been handled in a prescan above or being
diff --git a/gcc/opts.c b/gcc/opts.c
index c212a1a57dc..09b91aa2b87 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2730,6 +2730,10 @@ common_handle_option (struct gcc_options *opts,
   set_debug_level (NO_DEBUG, 2, arg, opts, opts_set, loc);
   break;
 
+case OPT_gcodeview:
+  set_debug_level (PDB_DEBUG, false, "", opts, opts_set, loc);
+  break;
+
 case OPT_gstabs:
 case OPT_gstabs_:
   set_debug_level (DBX_DEBUG, code == OPT_gstabs_, arg, opts, opts_set,
diff --git a/gcc/pdbout.c b/gcc/pdbout.c
new file mode 100644
index 000..e8f39bb64ea
--- /dev/null
+++ b/gcc/pdbout.c
@@ -0,0 +1,70 @@
+/* Output CodeView debugging information from GNU compiler.
+ * Copyright (C) 2021 Mark Harmstone
+ *
+ * This file is part of GCC.
+ *
+ * GCC is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any later
+ * version.
+ *
+ * GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GCC; see the file COPYING3.  If not see
+ * <http://www.gnu.org/licenses/>.  */
+
+/* The CodeView structure is partially documented - definitions of structures
+ * output below can be found at:
+ * https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h
+ */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "debug.h"
+#include "pdbout.h"
+
+const struct gcc_debug_hooks pdb_debug_hooks = {
+  debug_nothing_charstar,  /* init */
+  debug_nothing_charstar,  /* finish */
+  debug_nothing_charstar, 

[PATCH 03/24] pdbout: Output function details.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 149 +--
 gcc/pdbout.h |  12 +
 2 files changed, 158 insertions(+), 3 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index feaab37cc37..17011134d7a 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -29,14 +29,25 @@
 #include "tree.h"
 #include "debug.h"
 #include "pdbout.h"
+#include "function.h"
 #include "output.h"
 #include "target.h"
 
+#define FUNC_BEGIN_LABEL   ".Lstartfunc"
+#define FUNC_END_LABEL ".Lendfunc"
+
+static void pdbout_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
+  unsigned int column ATTRIBUTE_UNUSED,
+  const char *file ATTRIBUTE_UNUSED);
+static void pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
+const char *file ATTRIBUTE_UNUSED);
 static void pdbout_finish (const char *filename);
+static void pdbout_begin_function (tree func);
 static void pdbout_late_global_decl (tree var);
 
 static struct pdb_type *find_type (tree t);
 
+static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
 static hash_table  tree_hash_table (31);
@@ -66,11 +77,11 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_int_int,   /* end_block */
   debug_true_const_tree,   /* ignore_block */
   debug_nothing_int_int_charstar_int_bool, /* source_line */
-  debug_nothing_int_int_charstar,  /* begin_prologue */
+  pdbout_begin_prologue,
   debug_nothing_int_charstar,  /* end_prologue */
   debug_nothing_int_charstar,  /* begin_epilogue */
-  debug_nothing_int_charstar,  /* end_epilogue */
-  debug_nothing_tree,  /* begin_function */
+  pdbout_end_epilogue,
+  pdbout_begin_function,
   debug_nothing_int,   /* end_function */
   debug_nothing_tree,  /* register_main_translation_unit */
   debug_nothing_tree,  /* function_decl */
@@ -93,6 +104,84 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   TYPE_SYMTAB_IS_ADDRESS   /* tree_type_symtab_field */
 };
 
+/* Add label before function start */
+static void
+pdbout_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
+  unsigned int column ATTRIBUTE_UNUSED,
+  const char *file ATTRIBUTE_UNUSED)
+{
+  fprintf (asm_out_file, FUNC_BEGIN_LABEL "%u:\n",
+  current_function_funcdef_no);
+}
+
+/* Add label after function end */
+static void
+pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
+const char *file ATTRIBUTE_UNUSED)
+{
+  fprintf (asm_out_file, FUNC_END_LABEL "%u:\n", current_function_funcdef_no);
+}
+
+/* Output PROCSYM32 structure, which describes a global function (S_GPROC32)
+ * or a local (i.e. static) one (S_LPROC32). */
+static void
+pdbout_proc32 (struct pdb_func *func)
+{
+  size_t name_len = func->name ? strlen (func->name) : 0;
+  uint16_t len = 40 + name_len, align;
+
+  // start procedure
+
+  if (len % 4 != 0)
+{
+  align = 4 - (len % 4);
+  len += 4 - (len % 4);
+}
+  else
+align = 0;
+
+  fprintf (asm_out_file, ".Lcvprocstart%u:\n", func->num);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));   // reclen
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  func->public_flag ? S_GPROC32 : S_LPROC32);
+  fprintf (asm_out_file, "\t.long\t0\n");  // pParent
+  fprintf (asm_out_file, "\t.long\t[.Lcvprocend%u]-[.debug$S]\n",
+  func->num);  // pEnd
+  fprintf (asm_out_file, "\t.long\t0\n");  // pNext
+  fprintf (asm_out_file,
+  "\t.long\t[" FUNC_END_LABEL "%u]-[" FUNC_BEGIN_LABEL "%u]\n",
+  func->num, func->num);   // len
+  fprintf (asm_out_file, "\t.long\t0\n");  // DbgStart
+  fprintf (asm_out_file, "\t.long\t0\n");  // DbgEnd
+  fprintf (asm_out_file, "\t.short\t0x%x\n", func->type ? func->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+
+  fprintf (asm_out_file, "\t.secrel32\t" FUNC_BEGIN_LABEL "%u\n",
+  func->num);  // offset
+  fprintf (asm_out_file, "\t.secidx\t" FUNC_BEGIN_LABEL "%u\n",
+  func->num);  // section
+
+  fprintf (asm_out_file, "\t.byte\t0\n");  // flags
+
+  if (func->name)
+ASM_OUTPUT_ASCII (asm_out_file, func->name, name_len + 1);
+  else
+fprintf (asm_out_file, "\t.byte\t0\n");
+
+  for (unsigned int i = 0; i < align; i++)
+{
+  fprintf (asm_out_file, "\t.byte\t0\n");
+}
+
+  // end procedure
+
+  fprintf (asm_out_file, ".Lcvprocend%u:\n", func->num);
+
+  fprintf (asm_out_file, "\t.short\t0x2\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", S_END);
+}
+
 /* Output DATASYM32 structure, describing a global variable: either
  * one with file-level scope (S_LDATA32) or global scope (S_GDATA32). */
 static void
@@ -138,6 +227,8 @@ pdbout_data32 (struct pdb_global_var *v)
 static void
 write_pdb_sec

[PATCH 02/24] pdbout: Output details of local variables.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 417 ++-
 gcc/pdbout.h |  89 +++
 2 files changed, 504 insertions(+), 2 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index e8f39bb64ea..feaab37cc37 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -25,13 +25,37 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "varasm.h"
 #include "tree.h"
 #include "debug.h"
 #include "pdbout.h"
+#include "output.h"
+#include "target.h"
+
+static void pdbout_finish (const char *filename);
+static void pdbout_late_global_decl (tree var);
+
+static struct pdb_type *find_type (tree t);
+
+static struct pdb_global_var *global_vars = NULL;
+static struct pdb_type *types = NULL, *last_type = NULL;
+static hash_table  tree_hash_table (31);
+static struct pdb_type *byte_type, *signed_byte_type, *wchar_type,
+  *char16_type, *uint16_type, *int16_type, *char32_type, *uint32_type,
+  *int32_type, *uint64_type, *int64_type, *uint128_type, *int128_type,
+  *long_type, *ulong_type, *hresult_type;
+static struct pdb_type *float16_type, *float32_type, *float48_type,
+  *float64_type, *float80_type, *float128_type;
+static struct pdb_type *bool8_type, *bool16_type, *bool32_type, *bool64_type,
+  *bool128_type;
+static struct pdb_type *complex16_type, *complex32_type, *complex48_type,
+  *complex64_type, *complex80_type, *complex128_type;
+static struct pdb_type *void_type, *nullptr_type;
+static bool builtins_initialized = false;
 
 const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_charstar,  /* init */
-  debug_nothing_charstar,  /* finish */
+  pdbout_finish,
   debug_nothing_charstar,  /* early_finish */
   debug_nothing_void,  /* assembly_start */
   debug_nothing_int_charstar,  /* define */
@@ -51,7 +75,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_tree,  /* register_main_translation_unit */
   debug_nothing_tree,  /* function_decl */
   debug_nothing_tree,  /* early_global_decl */
-  debug_nothing_tree,  /* late_global_decl */
+  pdbout_late_global_decl,
   debug_nothing_tree_int,  /* type_decl */
   debug_nothing_tree_tree_tree_bool_bool,  /* imported_module_or_decl */
   debug_false_tree_charstarstar_uhwistar,  /* die_ref_for_decl */
@@ -68,3 +92,392 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   0,   /* start_end_main_source_file */
   TYPE_SYMTAB_IS_ADDRESS   /* tree_type_symtab_field */
 };
+
+/* Output DATASYM32 structure, describing a global variable: either
+ * one with file-level scope (S_LDATA32) or global scope (S_GDATA32). */
+static void
+pdbout_data32 (struct pdb_global_var *v)
+{
+  size_t name_len = strlen (v->name);
+  uint16_t len;
+
+  // Outputs DATASYM32 struct
+
+  len = 15 + name_len;
+
+  if (len % 4 != 0)
+len += 4 - (len % 4);
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));   // reclen
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  v->public_flag ? S_GDATA32 : S_LDATA32);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", v->type ? v->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");
+
+  fprintf (asm_out_file, "\t.secrel32\t"); // off
+  ASM_OUTPUT_LABELREF (asm_out_file, v->asm_name);
+  fprintf (asm_out_file, "\n");
+  fprintf (asm_out_file, "\t.secidx\t");   // section
+  ASM_OUTPUT_LABELREF (asm_out_file, v->asm_name);
+  fprintf (asm_out_file, "\n");
+
+  ASM_OUTPUT_ASCII (asm_out_file, v->name, name_len + 1);
+
+  fprintf (asm_out_file, "\t.balign\t4\n");
+}
+
+/* Output the .debug$S section, which has everything except the
+ * type definitions (global variables, functions, string table,
+ * file checksums, line numbers).
+ * The linker will extract this section from all the object
+ * files, remove any duplicate data, resolve all addresses,
+ * and output the resulting data into a PDB file. The section's
+ * marked as "ndr", so even if the linker doesn't understand it,
+ * the section won't make its way into final binary. */
+static void
+write_pdb_section (void)
+{
+  fprintf (asm_out_file, "\t.section\t.debug$S, \"ndr\"\n");
+  fprintf (asm_out_file, "\t.long\t0x%x\n", CV_SIGNATURE_C13);
+  fprintf (asm_out_file, "\t.long\t0x%x\n", DEBUG_S_SYMBOLS);
+  fprintf (asm_out_file, "\t.long\t[.Lsymend]-[.Lsymstart]\n");
+
+  fprintf (asm_out_file, ".Lsymstart:\n");
+
+  while (global_vars)
+{
+  struct pdb_global_var *n;
+
+  pdbout_data32 (global_vars);
+
+  n = global_vars->next;
+
+  if (global_vars->name)
+   free (global_vars->name);
+
+  if (global_vars->asm_name)
+   free (global_vars->asm_name);
+
+  free (global_vars);
+
+  global_vars = n;
+}
+
+  fprintf (asm_out_file, ".Lsymend:\n");
+}
+
+/* We've finished compilation - output the .debug$S section
+ * to the asm file. */
+static void
+pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
+{
+  write_pdb_section ();
+}
+

[PATCH 05/24] pdbout: Handle optimized variables.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 242 ++-
 gcc/pdbout.h |  21 +
 2 files changed, 260 insertions(+), 3 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 2f5b52b6fc3..29b0d1c131f 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -51,6 +51,7 @@ static void pdbout_finish (const char *filename);
 static void pdbout_begin_function (tree func);
 static void pdbout_late_global_decl (tree var);
 static void pdbout_function_decl (tree decl);
+static void pdbout_var_location (rtx_insn * loc_note);
 static void pdbout_begin_block (unsigned int line ATTRIBUTE_UNUSED,
unsigned int blocknum);
 static void pdbout_end_block (unsigned int line ATTRIBUTE_UNUSED,
@@ -62,6 +63,7 @@ static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
+static unsigned int var_loc_number = 1;
 static hash_table  tree_hash_table (31);
 static struct pdb_type *byte_type, *signed_byte_type, *wchar_type,
   *char16_type, *uint16_type, *int16_type, *char32_type, *uint32_type,
@@ -107,7 +109,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_tree,  /* outlining_inline_function */
   debug_nothing_rtx_code_label,/* label */
   debug_nothing_int,   /* handle_pch */
-  debug_nothing_rtx_insn,  /* var_location */
+  pdbout_var_location,
   debug_nothing_tree,  /* inline_entry */
   debug_nothing_tree,  /* size_function */
   debug_nothing_void,  /* switch_text_section */
@@ -134,12 +136,143 @@ pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
   fprintf (asm_out_file, FUNC_END_LABEL "%u:\n", current_function_funcdef_no);
 }
 
+/* Output DEFRANGESYMREGISTER or DEFRANGESYMREGISTERREL structure, describing
+ * the scope range, register, and offset at which a local variable can be
+ * found. */
+static void
+write_var_location (struct pdb_var_location *var_loc,
+   unsigned int next_var_loc_number, unsigned int func_num)
+{
+  switch (var_loc->type)
+{
+case pdb_var_loc_register:
+  fprintf (asm_out_file, "\t.short\t0xe\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", S_DEFRANGE_REGISTER);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", var_loc->reg);
+  fprintf (asm_out_file, "\t.short\t0\n"); // range attr
+  fprintf (asm_out_file, "\t.secrel32\t.Lvarloc%u\n",
+  var_loc->var_loc_number);
+  fprintf (asm_out_file, "\t.secidx\t.Lvarloc%u\n",
+  var_loc->var_loc_number);
+
+  if (next_var_loc_number != 0)
+   {
+ fprintf (asm_out_file, "\t.short\t[.Lvarloc%u]-[.Lvarloc%u]\n",
+  next_var_loc_number, var_loc->var_loc_number);
+   }
+  else
+   {
+ fprintf (asm_out_file,
+  "\t.short\t[" FUNC_END_LABEL "%u]-[.Lvarloc%u]\n",
+  func_num, var_loc->var_loc_number);  // to end of function
+   }
+
+  break;
+
+case pdb_var_loc_regrel:
+  fprintf (asm_out_file, "\t.short\t0x12\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", S_DEFRANGE_REGISTER_REL);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", var_loc->reg);
+
+  // spilledUdtMember, padding, offsetParent
+  fprintf (asm_out_file, "\t.short\t0\n");
+
+  fprintf (asm_out_file, "\t.long\t0x%x\n", var_loc->offset);
+  fprintf (asm_out_file, "\t.secrel32\t.Lvarloc%u\n",
+  var_loc->var_loc_number);
+  fprintf (asm_out_file, "\t.secidx\t.Lvarloc%u\n",
+  var_loc->var_loc_number);
+
+  if (next_var_loc_number != 0)
+   {
+ fprintf (asm_out_file, "\t.short\t[.Lvarloc%u]-[.Lvarloc%u]\n",
+  next_var_loc_number, var_loc->var_loc_number);
+   }
+  else
+   {
+ fprintf (asm_out_file,
+  "\t.short\t[" FUNC_END_LABEL "%u]-[.Lvarloc%u]\n",
+  func_num, var_loc->var_loc_number);  // to end of function
+   }
+
+  break;
+
+case pdb_var_loc_unknown:
+  break;
+}
+}
+
+/* We have encountered an optimized local variable, i.e. one which doesn't
+ * live in the same place for the duration of a function.
+ * Output a LOCALSYM struct. */
+static void
+pdbout_optimized_local_variable (struct pdb_local_var *v,
+struct pdb_var_location *var_locs,
+unsigned int func_num)
+{
+  uint16_t len, align;
+  size_t name_len = strlen (v->name);
+  struct pdb_var_location *last_pvl = var_locs, *pvl;
+
+  len = 11 + name_len;
+
+  if (len % 4 != 0)
+{
+  align = 4 - (len % 4);
+  len += 4 - (len % 4);
+}
+  else
+align = 0;
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));
+  fprintf (asm_out_file, "\t.short\t0x%x\n", S_LOCAL);
+  fprintf (asm_out_file, "\t.long\t0x%x\n", v->type ? v->type-

[PATCH 07/24] pdbout: Output line numbers.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 150 ++-
 gcc/pdbout.h |  10 
 2 files changed, 159 insertions(+), 1 deletion(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index a4424fa470d..040ac6fe8e4 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -54,6 +54,11 @@ static void pdbout_begin_function (tree func);
 static void pdbout_late_global_decl (tree var);
 static void pdbout_start_source_file (unsigned int line ATTRIBUTE_UNUSED,
  const char *file);
+static void pdbout_source_line (unsigned int line,
+   unsigned int column ATTRIBUTE_UNUSED,
+   const char *text,
+   int discriminator ATTRIBUTE_UNUSED,
+   bool is_stmt ATTRIBUTE_UNUSED);
 static void pdbout_function_decl (tree decl);
 static void pdbout_var_location (rtx_insn * loc_note);
 static void pdbout_begin_block (unsigned int line ATTRIBUTE_UNUSED,
@@ -69,6 +74,7 @@ static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
+static unsigned int num_line_number_entries = 0;
 static unsigned int num_source_files = 0;
 static unsigned int var_loc_number = 1;
 static hash_table  tree_hash_table (31);
@@ -97,7 +103,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   pdbout_begin_block,
   pdbout_end_block,
   debug_true_const_tree,   /* ignore_block */
-  debug_nothing_int_int_charstar_int_bool, /* source_line */
+  pdbout_source_line,
   pdbout_begin_prologue,
   debug_nothing_int_charstar,  /* end_prologue */
   debug_nothing_int_charstar,  /* begin_epilogue */
@@ -617,6 +623,88 @@ write_file_checksums ()
   fprintf (asm_out_file, ".Lchksumsend:\n");
 }
 
+/* Loop through each function, and output the line number to
+ * address mapping. */
+static void
+write_line_numbers ()
+{
+  struct pdb_func *func = funcs;
+  unsigned int lines_part = 0;
+
+  while (func)
+{
+  while (func->lines)
+   {
+ struct pdb_line *l, *last_line;
+ unsigned int num_entries = 0, source_file, first_entry;
+
+ source_file = func->lines->source_file;
+
+ l = last_line = func->lines;
+ while (l && l->source_file == source_file)
+   {
+ num_entries++;
+ last_line = l;
+ l = l->next;
+   }
+
+ first_entry = func->lines->entry;
+
+ fprintf (asm_out_file, "\t.long\t0x%x\n", DEBUG_S_LINES);
+ fprintf (asm_out_file, "\t.long\t[.Llinesend%u]-[.Llinesstart%u]\n",
+  lines_part, lines_part);
+ fprintf (asm_out_file, ".Llinesstart%u:\n", lines_part);
+
+ // offset
+ fprintf (asm_out_file, "\t.secrel32\t.Lline%u\n", first_entry);
+ // section
+ fprintf (asm_out_file, "\t.secidx\t.Lline%u\n", first_entry);
+
+ fprintf (asm_out_file, "\t.short\t0\n");  // flags
+
+ // next section of function is another source file
+ if (last_line->next)
+   {
+ fprintf (asm_out_file, "\t.long\t[.Lline%u]-[.Lline%u]\n",
+  last_line->next->entry, first_entry);// length
+   }
+ else
+   {
+ fprintf (asm_out_file,
+  "\t.long\t[" FUNC_END_LABEL "%u]-[.Lline%u]\n",
+  func->num, first_entry); // length
+   }
+
+ // file ID (0x18 is size of checksum struct)
+ fprintf (asm_out_file, "\t.long\t0x%x\n", source_file * 0x18);
+ fprintf (asm_out_file, "\t.long\t0x%x\n", num_entries);
+ // length of file block
+ fprintf (asm_out_file, "\t.long\t0x%x\n", 0xc + (num_entries * 8));
+
+ while (func->lines && func->lines->source_file == source_file)
+   {
+ struct pdb_line *n = func->lines->next;
+
+ // offset
+ fprintf (asm_out_file, "\t.long\t[.Lline%u]-[.Lline%u]\n",
+  func->lines->entry, first_entry);
+
+ // line no.
+ fprintf (asm_out_file, "\t.long\t0x%x\n", func->lines->line);
+
+ free (func->lines);
+
+ func->lines = n;
+   }
+
+ fprintf (asm_out_file, ".Llinesend%u:\n", lines_part);
+ lines_part++;
+   }
+
+  func = func->next;
+}
+}
+
 /* Output the .debug$S section, which has everything except the
  * type definitions (global variables, functions, string table,
  * file checksums, line numbers).
@@ -688,6 +776,8 @@ write_pdb_section (void)
 
   write_file_checksums ();
 
+  write_line_numbers ();
+
   while (funcs)
 {
   struct pdb_func *n = funcs->next;
@@ -728,6 +818,7 @@ get_tree_name (tree t)
 static void
 pdbout_begin_function (tree func)
 {
+  expanded_location xloc;
   struct pdb_func *f = (struct pdb_func *) xmal

[PATCH 06/24] pdbout: Output checksums and names of source files.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 195 ++-
 gcc/pdbout.h |  16 +
 2 files changed, 209 insertions(+), 2 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 29b0d1c131f..a4424fa470d 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -32,6 +32,7 @@
 #include "function.h"
 #include "output.h"
 #include "target.h"
+#include "md5.h"
 #include "rtl.h"
 #include "insn-config.h"
 #include "reload.h"
@@ -47,9 +48,12 @@ static void pdbout_begin_prologue (unsigned int line 
ATTRIBUTE_UNUSED,
   const char *file ATTRIBUTE_UNUSED);
 static void pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
 const char *file ATTRIBUTE_UNUSED);
+static void pdbout_init (const char *filename);
 static void pdbout_finish (const char *filename);
 static void pdbout_begin_function (tree func);
 static void pdbout_late_global_decl (tree var);
+static void pdbout_start_source_file (unsigned int line ATTRIBUTE_UNUSED,
+ const char *file);
 static void pdbout_function_decl (tree decl);
 static void pdbout_var_location (rtx_insn * loc_note);
 static void pdbout_begin_block (unsigned int line ATTRIBUTE_UNUSED,
@@ -63,6 +67,9 @@ static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
+static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
+static uint32_t source_file_string_offset = 1;
+static unsigned int num_source_files = 0;
 static unsigned int var_loc_number = 1;
 static hash_table  tree_hash_table (31);
 static struct pdb_type *byte_type, *signed_byte_type, *wchar_type,
@@ -79,13 +86,13 @@ static struct pdb_type *void_type, *nullptr_type;
 static bool builtins_initialized = false;
 
 const struct gcc_debug_hooks pdb_debug_hooks = {
-  debug_nothing_charstar,  /* init */
+  pdbout_init,
   pdbout_finish,
   debug_nothing_charstar,  /* early_finish */
   debug_nothing_void,  /* assembly_start */
   debug_nothing_int_charstar,  /* define */
   debug_nothing_int_charstar,  /* undef */
-  debug_nothing_int_charstar,  /* start_source_file */
+  pdbout_start_source_file,
   debug_nothing_int,   /* end_source_file */
   pdbout_begin_block,
   pdbout_end_block,
@@ -576,6 +583,40 @@ pdbout_data32 (struct pdb_global_var *v)
   fprintf (asm_out_file, "\t.balign\t4\n");
 }
 
+/* Output names of the files which make up this translation unit,
+ * along with their MD5 checksums. */
+static void
+write_file_checksums ()
+{
+  fprintf (asm_out_file, "\t.long\t0x%x\n", DEBUG_S_FILECHKSMS);
+  fprintf (asm_out_file, "\t.long\t[.Lchksumsend]-[.Lchksumsstart]\n");
+  fprintf (asm_out_file, ".Lchksumsstart:\n");
+
+  while (source_files)
+{
+  struct pdb_source_file *n;
+
+  fprintf (asm_out_file, "\t.long\t0x%x\n", source_files->str_offset);
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", 16);   // length of MD5 hash
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", CHKSUM_TYPE_MD5);
+
+  for (unsigned int i = 0; i < 16; i++)
+   {
+ fprintf (asm_out_file, "\t.byte\t0x%x\n", source_files->hash[i]);
+   }
+
+  fprintf (asm_out_file, "\t.short\t0\n");
+
+  n = source_files->next;
+
+  free (source_files);
+
+  source_files = n;
+}
+
+  fprintf (asm_out_file, ".Lchksumsend:\n");
+}
+
 /* Output the .debug$S section, which has everything except the
  * type definitions (global variables, functions, string table,
  * file checksums, line numbers).
@@ -587,6 +628,7 @@ pdbout_data32 (struct pdb_global_var *v)
 static void
 write_pdb_section (void)
 {
+  struct pdb_source_file *psf;
   struct pdb_func *func;
 
   fprintf (asm_out_file, "\t.section\t.debug$S, \"ndr\"\n");
@@ -625,6 +667,27 @@ write_pdb_section (void)
 
   fprintf (asm_out_file, ".Lsymend:\n");
 
+  fprintf (asm_out_file, "\t.long\t0x%x\n", DEBUG_S_STRINGTABLE);
+  fprintf (asm_out_file, "\t.long\t[.Lstrtableend]-[.Lstrtablestart]\n");
+  fprintf (asm_out_file, ".Lstrtablestart:\n");
+  fprintf (asm_out_file, "\t.byte\t0\n");
+
+  psf = source_files;
+  while (psf)
+{
+  size_t name_len = strlen (psf->name);
+
+  ASM_OUTPUT_ASCII (asm_out_file, psf->name + name_len + 1,
+   strlen (psf->name + name_len + 1) + 1);
+
+  psf = psf->next;
+}
+
+  fprintf (asm_out_file, "\t.balign\t4\n");
+  fprintf (asm_out_file, ".Lstrtableend:\n");
+
+  write_file_checksums ();
+
   while (funcs)
 {
   struct pdb_func *n = funcs->next;
@@ -993,6 +1056,134 @@ find_type (tree t)
 return NULL;
 }
 
+#ifndef _WIN32
+/* Given a Unix-style path, construct a fake Windows path, which is what windbg
+ * and Visual Studio are expecting. This maps / to Z:\, which is the default
+ * behaviour on Wine. */
+static char *
+make_windows_path (char *src)
+{
+  size_t len = strlen (src);
+  cha

[PATCH 04/24] pdbout: Output details of variables within functions.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 1272 +-
 gcc/pdbout.h |  969 ++
 2 files changed, 2238 insertions(+), 3 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 17011134d7a..2f5b52b6fc3 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -32,6 +32,12 @@
 #include "function.h"
 #include "output.h"
 #include "target.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "reload.h"
+#include "cp/cp-tree.h"
+#include "common/common-target.h"
+#include "except.h"
 
 #define FUNC_BEGIN_LABEL   ".Lstartfunc"
 #define FUNC_END_LABEL ".Lendfunc"
@@ -44,10 +50,16 @@ static void pdbout_end_epilogue (unsigned int line 
ATTRIBUTE_UNUSED,
 static void pdbout_finish (const char *filename);
 static void pdbout_begin_function (tree func);
 static void pdbout_late_global_decl (tree var);
+static void pdbout_function_decl (tree decl);
+static void pdbout_begin_block (unsigned int line ATTRIBUTE_UNUSED,
+   unsigned int blocknum);
+static void pdbout_end_block (unsigned int line ATTRIBUTE_UNUSED,
+ unsigned int blocknum);
 
 static struct pdb_type *find_type (tree t);
 
 static struct pdb_func *funcs = NULL, *cur_func = NULL;
+static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
 static hash_table  tree_hash_table (31);
@@ -73,8 +85,8 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_int_charstar,  /* undef */
   debug_nothing_int_charstar,  /* start_source_file */
   debug_nothing_int,   /* end_source_file */
-  debug_nothing_int_int,   /* begin_block */
-  debug_nothing_int_int,   /* end_block */
+  pdbout_begin_block,
+  pdbout_end_block,
   debug_true_const_tree,   /* ignore_block */
   debug_nothing_int_int_charstar_int_bool, /* source_line */
   pdbout_begin_prologue,
@@ -84,7 +96,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   pdbout_begin_function,
   debug_nothing_int,   /* end_function */
   debug_nothing_tree,  /* register_main_translation_unit */
-  debug_nothing_tree,  /* function_decl */
+  pdbout_function_decl,
   debug_nothing_tree,  /* early_global_decl */
   pdbout_late_global_decl,
   debug_nothing_tree_int,  /* type_decl */
@@ -122,6 +134,198 @@ pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
   fprintf (asm_out_file, FUNC_END_LABEL "%u:\n", current_function_funcdef_no);
 }
 
+/* Output the information as to where to a local variable can be found. */
+static void
+pdbout_local_variable (struct pdb_local_var *v)
+{
+  uint16_t len, align;
+  size_t name_len = strlen (v->name);
+
+  switch (v->var_type)
+{
+case pdb_local_var_regrel:
+  if (v->reg == CV_X86_EBP) // ebp is a special case
+   {
+ len = 13 + name_len;
+
+ if (len % 4 != 0)
+   {
+ align = 4 - (len % 4);
+ len += 4 - (len % 4);
+   }
+ else
+   align = 0;
+
+ /* Output BPRELSYM32 struct */
+
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));   // reclen
+ fprintf (asm_out_file, "\t.short\t0x%x\n", S_BPREL32);
+ fprintf (asm_out_file, "\t.long\t0x%x\n", v->offset);
+ fprintf (asm_out_file, "\t.long\t0x%x\n",
+  v->type ? v->type->id : 0);
+
+ ASM_OUTPUT_ASCII (asm_out_file, v->name, name_len + 1);
+   }
+  else
+   {
+ len = 15 + name_len;
+
+ if (len % 4 != 0)
+   {
+ align = 4 - (len % 4);
+ len += 4 - (len % 4);
+   }
+ else
+   align = 0;
+
+ /* Output REGREL32 struct */
+
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));   // reclen
+ fprintf (asm_out_file, "\t.short\t0x%x\n", S_REGREL32);
+ fprintf (asm_out_file, "\t.long\t0x%x\n", v->offset);
+ fprintf (asm_out_file, "\t.long\t0x%x\n",
+  v->type ? v->type->id : 0);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", v->reg);
+
+ ASM_OUTPUT_ASCII (asm_out_file, v->name, name_len + 1);
+   }
+
+  for (unsigned int i = 0; i < align; i++)
+   {
+ fprintf (asm_out_file, "\t.byte\t0\n");
+   }
+  break;
+
+case pdb_local_var_register:
+  len = 11 + name_len;
+
+  if (len % 4 != 0)
+   {
+ align = 4 - (len % 4);
+ len += 4 - (len % 4);
+   }
+  else
+   align = 0;
+
+  /* Output REGSYM struct */
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));   // reclen
+  fprintf (asm_out_file, "\t.short\t0x%x\n", S_REGISTER);
+  fprintf (asm_out_file, "\t.long\t0x%x\n",
+  v->type ? v->type->id : 0);
+  fprintf (asm_

[PATCH 08/24] pdbout: Output function prototypes.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 338 ++-
 gcc/pdbout.h |  23 
 2 files changed, 359 insertions(+), 2 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 040ac6fe8e4..64f7c1d71bc 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -32,6 +32,7 @@
 #include "function.h"
 #include "output.h"
 #include "target.h"
+#include "config/i386/i386-protos.h"
 #include "md5.h"
 #include "rtl.h"
 #include "insn-config.h"
@@ -43,6 +44,8 @@
 #define FUNC_BEGIN_LABEL   ".Lstartfunc"
 #define FUNC_END_LABEL ".Lendfunc"
 
+#define FIRST_TYPE_NUM 0x1000
+
 static void pdbout_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
   unsigned int column ATTRIBUTE_UNUSED,
   const char *file ATTRIBUTE_UNUSED);
@@ -72,6 +75,8 @@ static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
+static struct pdb_type *arglist_types = NULL;
+static struct pdb_type *proc_types = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
 static unsigned int num_line_number_entries = 0;
@@ -791,12 +796,142 @@ write_pdb_section (void)
 }
 }
 
-/* We've finished compilation - output the .debug$S section
+/* Free a pdb_type that we've allocated. */
+static void
+free_type (struct pdb_type *t)
+{
+  free (t);
+}
+
+/* Output a lfArgList structure, describing the arguments that a
+ * procedure expects. */
+static void
+write_arglist (struct pdb_arglist *arglist)
+{
+  unsigned int len = 8 + (4 * arglist->count);
+
+  if (arglist->count == 0) // zero-length arglist has dummy entry
+len += 4;
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_ARGLIST);
+  fprintf (asm_out_file, "\t.long\t0x%x\n",
+  arglist->count == 0 ? 1 : arglist->count);
+
+  for (unsigned int i = 0; i < arglist->count; i++)
+{
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  arglist->args[i] ? arglist->args[i]->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+}
+
+  if (arglist->count == 0)
+{
+  fprintf (asm_out_file, "\t.short\t0\n"); // empty type
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+}
+}
+
+/* Output a lfProc structure, which describes the prototype of a
+ * procedure. See also pdbout_proc32, which outputs the details of
+ * a specific procedure. */
+static void
+write_procedure (struct pdb_proc *proc)
+{
+  fprintf (asm_out_file, "\t.short\t0xe\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_PROCEDURE);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  proc->return_type ? proc->return_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", proc->calling_convention);
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", proc->attributes);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", proc->num_args);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  proc->arg_list ? proc->arg_list->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+}
+
+/* Given a pdb_type, output its definition. */
+static void
+write_type (struct pdb_type *t)
+{
+  switch (t->cv_type)
+{
+case LF_ARGLIST:
+  write_arglist ((struct pdb_arglist *) t->data);
+  break;
+
+case LF_PROCEDURE:
+  write_procedure ((struct pdb_proc *) t->data);
+  break;
+}
+}
+
+/* Output the .debug$T section, which contains all the types used. */
+static void
+write_pdb_type_section (void)
+{
+  struct pdb_type *n;
+
+  fprintf (asm_out_file, "\t.section\t.debug$T, \"ndr\"\n");
+  fprintf (asm_out_file, "\t.long\t0x%x\n", CV_SIGNATURE_C13);
+
+  n = types;
+  while (n)
+{
+  write_type (n);
+
+  n = n->next;
+}
+
+  while (types)
+{
+  n = types->next;
+
+  free_type (types);
+
+  types = n;
+}
+}
+
+/* Loop through our types and assign them sequential numbers. */
+static void
+number_types (void)
+{
+  struct pdb_type *t;
+  uint16_t type_num = FIRST_TYPE_NUM;
+
+  t = types;
+  while (t)
+{
+  if (t->id != 0)
+   {
+ t = t->next;
+ continue;
+   }
+
+  t->id = type_num;
+  type_num++;
+
+  if (type_num == 0)   // overflow
+   {
+ fprintf (stderr, "too many CodeView types\n");
+ xexit (1);
+   }
+
+  t = t->next;
+}
+}
+
+/* We've finished compilation - output the .debug$S and .debug$T sections
  * to the asm file. */
 static void
 pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
 {
+  number_types ();
+
   write_pdb_section ();
+  write_pdb_type_section ();
 }
 
 /* For a tree t, construct the name. */
@@ -877,6 +1012,197 @@ pdbout_late_global_decl (tree var)
   global_vars = v;
 }
 
+/* Ad

[PATCH 09/24] pdbout: Output information about pointers.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 131 ---
 gcc/pdbout.h |  36 ++
 2 files changed, 161 insertions(+), 6 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 64f7c1d71bc..08bb14364e5 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -76,6 +76,7 @@ static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
 static struct pdb_type *arglist_types = NULL;
+static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
@@ -803,6 +804,17 @@ free_type (struct pdb_type *t)
   free (t);
 }
 
+/* Output a lfPointer structure. */
+static void
+write_pointer (struct pdb_pointer *ptr)
+{
+  fprintf (asm_out_file, "\t.short\t0xa\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_POINTER);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", ptr->type ? ptr->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.long\t0x%x\n", ptr->attr.num);
+}
+
 /* Output a lfArgList structure, describing the arguments that a
  * procedure expects. */
 static void
@@ -857,6 +869,13 @@ write_type (struct pdb_type *t)
 {
   switch (t->cv_type)
 {
+case LF_POINTER:
+  if (t->id < FIRST_TYPE_NUM)  // pointer to builtin
+   return;
+
+  write_pointer ((struct pdb_pointer *) t->data);
+  break;
+
 case LF_ARGLIST:
   write_arglist ((struct pdb_arglist *) t->data);
   break;
@@ -910,13 +929,38 @@ number_types (void)
  continue;
}
 
-  t->id = type_num;
-  type_num++;
-
-  if (type_num == 0)   // overflow
+  switch (t->cv_type)
{
- fprintf (stderr, "too many CodeView types\n");
- xexit (1);
+   case LF_POINTER:
+ {
+   struct pdb_pointer *ptr = (struct pdb_pointer *) t->data;
+
+   // pointers to builtins have their own constants
+   if (ptr->type && ptr->type->id != 0 && ptr->type->id < 0x100)
+ {
+   if (ptr->attr.s.ptrtype == CV_PTR_NEAR32)
+ {
+   t->id = (CV_TM_NPTR32 << 8) | ptr->type->id;
+   break;
+ }
+   else if (ptr->attr.s.ptrtype == CV_PTR_64)
+ {
+   t->id = (CV_TM_NPTR64 << 8) | ptr->type->id;
+   break;
+ }
+ }
+   [[fallthrough]];
+ }
+
+   default:
+ t->id = type_num;
+ type_num++;
+
+ if (type_num == 0)// overflow
+   {
+ fprintf (stderr, "too many CodeView types\n");
+ xexit (1);
+   }
}
 
   t = t->next;
@@ -1072,6 +1116,77 @@ add_arglist_type (struct pdb_type *t)
   return t;
 }
 
+/* Given a pointer type t, allocate a new pdb_type and add it to the
+ * type list. */
+static struct pdb_type *
+find_type_pointer (tree t)
+{
+  struct pdb_type *ptrtype, *t2, *last_entry = NULL, *type;
+  struct pdb_pointer *ptr, v;
+  unsigned int size = TREE_INT_CST_ELT (TYPE_SIZE (t), 0) / 8;
+  struct pdb_type **slot;
+
+  type = find_type (TREE_TYPE (t));
+
+  if (!type)
+return NULL;
+
+  v.attr.num = 0;
+
+  v.attr.s.size = size;
+
+  if (size == 8)
+v.attr.s.ptrtype = CV_PTR_64;
+  else if (size == 4)
+v.attr.s.ptrtype = CV_PTR_NEAR32;
+
+  if (TREE_CODE (t) == REFERENCE_TYPE)
+v.attr.s.ptrmode =
+  TYPE_REF_IS_RVALUE (t) ? CV_PTR_MODE_RVREF : CV_PTR_MODE_LVREF;
+
+  t2 = pointer_types;
+  while (t2)
+{
+  ptr = (struct pdb_pointer *) t2->data;
+
+  if (ptr->type == type && ptr->attr.num == v.attr.num)
+   return t2;
+
+  last_entry = t2;
+  t2 = t2->next2;
+}
+
+  ptrtype =
+(struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+sizeof (struct pdb_pointer));
+  ptrtype->cv_type = LF_POINTER;
+  ptrtype->tree = t;
+  ptrtype->next = ptrtype->next2 = NULL;
+  ptrtype->id = 0;
+
+  ptr = (struct pdb_pointer *) ptrtype->data;
+  ptr->type = type;
+  ptr->attr.num = v.attr.num;
+
+  if (last_entry)
+last_entry->next2 = ptrtype;
+  else
+pointer_types = ptrtype;
+
+  if (last_type)
+last_type->next = ptrtype;
+  else
+types = ptrtype;
+
+  last_type = ptrtype;
+
+  slot =
+tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t), INSERT);
+  *slot = ptrtype;
+
+  return ptrtype;
+}
+
 /* Given a function type t, allocate a new pdb_type and add it to the
  * type list. */
 static struct pdb_type *
@@ -1478,6 +1593,10 @@ find_type (tree t)
 
   switch (TREE_CODE (t))
 {
+case POINTER_TYPE:
+case REFERENCE_TYPE:
+  return find_type_pointer (t);
+
 case FUNCTION_TYPE:
 case METHOD_TYPE:
   return find_type_function (t);
diff --git a/gcc/pdbout.h b/gcc/pdbou

[PATCH 10/24] pdbout: Output information about CV type modifiers.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 85 
 gcc/pdbout.h | 11 +++
 2 files changed, 96 insertions(+)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 08bb14364e5..fa3b1fb0312 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -78,6 +78,7 @@ static struct pdb_type *types = NULL, *last_type = NULL;
 static struct pdb_type *arglist_types = NULL;
 static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
+static struct pdb_type *modifier_types = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
 static unsigned int num_line_number_entries = 0;
@@ -863,6 +864,19 @@ write_procedure (struct pdb_proc *proc)
   fprintf (asm_out_file, "\t.short\t0\n"); // padding
 }
 
+/* Output lfModifier structure, representing a const or volatile version
+ * of an existing type. */
+static void
+write_modifier (struct pdb_modifier *t)
+{
+  fprintf (asm_out_file, "\t.short\t0xa\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_MODIFIER);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", t->type ? t->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.short\t0x%x\n", t->modifier);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+}
+
 /* Given a pdb_type, output its definition. */
 static void
 write_type (struct pdb_type *t)
@@ -883,6 +897,10 @@ write_type (struct pdb_type *t)
 case LF_PROCEDURE:
   write_procedure ((struct pdb_proc *) t->data);
   break;
+
+case LF_MODIFIER:
+  write_modifier ((struct pdb_modifier *) t->data);
+  break;
 }
 }
 
@@ -1318,6 +1336,68 @@ find_type_function (tree t)
   return proctype;
 }
 
+/* Given a CV-modified type t, allocate a new pdb_type modifying
+ * the base type, and add it to the type list. */
+static struct pdb_type *
+find_type_modifier (tree t)
+{
+  struct pdb_type *type, *last_entry = NULL, *base_type;
+  struct pdb_modifier *mod;
+  uint16_t modifier = 0;
+  struct pdb_type **slot;
+
+  base_type = find_type (TYPE_MAIN_VARIANT (t));
+
+  if (TYPE_READONLY (t))
+modifier |= CV_MODIFIER_CONST;
+
+  if (TYPE_VOLATILE (t))
+modifier |= CV_MODIFIER_VOLATILE;
+
+  type = modifier_types;
+  while (type)
+{
+  mod = (struct pdb_modifier *) type->data;
+
+  if (mod->type == base_type && mod->modifier == modifier)
+   return type;
+
+  last_entry = type;
+  type = type->next2;
+}
+
+  type =
+(struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+sizeof (struct pdb_modifier));
+  type->cv_type = LF_MODIFIER;
+  type->tree = t;
+  type->next = type->next2 = NULL;
+  type->id = 0;
+
+  mod = (struct pdb_modifier *) type->data;
+
+  mod->type = base_type;
+  mod->modifier = modifier;
+
+  if (last_entry)
+last_entry->next2 = type;
+  else
+modifier_types = type;
+
+  if (last_type)
+last_type->next = type;
+  else
+types = type;
+
+  last_type = type;
+
+  slot =
+tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t), INSERT);
+  *slot = type;
+
+  return type;
+}
+
 inline hashval_t
 pdb_type_tree_hasher::hash (pdb_type_tree_hasher::compare_type tree)
 {
@@ -1439,6 +1519,11 @@ find_type (tree t)
   if (type)
 return type;
 
+  // add modifier type if const or volatile
+
+  if (TYPE_READONLY (t) || TYPE_VOLATILE (t))
+return find_type_modifier (t);
+
   switch (TREE_CODE (t))
 {
 case INTEGER_TYPE:
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index a660728158e..1fa2b1ab2fa 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -21,6 +21,7 @@
 #define GCC_PDBOUT_H 1
 
 #define S_END  0x0006
+#define LF_MODIFIER0x1001
 #define LF_POINTER 0x1002
 #define LF_PROCEDURE   0x1008
 #define S_BLOCK32  0x1103
@@ -262,6 +263,16 @@ struct pdb_source_file
   char name[1];
 };
 
+#define CV_MODIFIER_CONST  0x1
+#define CV_MODIFIER_VOLATILE   0x2
+#define CV_MODIFIER_UNALIGNED  0x4
+
+struct pdb_modifier
+{
+  struct pdb_type *type;
+  uint16_t modifier;
+};
+
 enum pdb_x86_register
 {
   CV_X86_NONE = 0,
-- 
2.26.2



[PATCH 12/24] pdbout: Handle type declarations and typedefs.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 76 +++-
 gcc/pdbout.h | 22 +++
 2 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 8376b0e762c..5089203e339 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -55,6 +55,7 @@ static void pdbout_init (const char *filename);
 static void pdbout_finish (const char *filename);
 static void pdbout_begin_function (tree func);
 static void pdbout_late_global_decl (tree var);
+static void pdbout_type_decl (tree t, int local ATTRIBUTE_UNUSED);
 static void pdbout_start_source_file (unsigned int line ATTRIBUTE_UNUSED,
  const char *file);
 static void pdbout_source_line (unsigned int line,
@@ -80,6 +81,7 @@ static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
 static struct pdb_type *modifier_types = NULL;
 static struct pdb_type *array_types = NULL;
+static struct pdb_alias *aliases = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
 static unsigned int num_line_number_entries = 0;
@@ -98,6 +100,7 @@ static struct pdb_type *complex16_type, *complex32_type, 
*complex48_type,
   *complex64_type, *complex80_type, *complex128_type;
 static struct pdb_type *void_type, *nullptr_type;
 static bool builtins_initialized = false;
+static hash_table  alias_hash_table (31);
 
 const struct gcc_debug_hooks pdb_debug_hooks = {
   pdbout_init,
@@ -122,7 +125,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   pdbout_function_decl,
   debug_nothing_tree,  /* early_global_decl */
   pdbout_late_global_decl,
-  debug_nothing_tree_int,  /* type_decl */
+  pdbout_type_decl,
   debug_nothing_tree_tree_tree_bool_bool,  /* imported_module_or_decl */
   debug_false_tree_charstarstar_uhwistar,  /* die_ref_for_decl */
   debug_nothing_tree_charstar_uhwi,/* register_external_die */
@@ -999,6 +1002,17 @@ write_pdb_type_section (void)
 
   types = n;
 }
+
+  while (aliases)
+{
+  struct pdb_alias *n;
+
+  n = aliases->next;
+
+  free (aliases);
+
+  aliases = n;
+}
 }
 
 /* Loop through our types and assign them sequential numbers. */
@@ -1635,6 +1649,7 @@ static struct pdb_type *
 find_type (tree t)
 {
   struct pdb_type *type;
+  struct pdb_alias *al;
 
   if (!builtins_initialized)
 add_builtin_types ();
@@ -1642,6 +1657,13 @@ find_type (tree t)
   if (!t)
 return NULL;
 
+  // search through typedefs
+
+  al = alias_hash_table.find_with_hash (t, alias_hasher::hash (t));
+
+  if (al)
+return al->type;
+
   // search through existing types
 
   type = tree_hash_table.find_with_hash (t, pdb_type_tree_hasher::hash (t));
@@ -1824,6 +1846,58 @@ find_type (tree t)
 }
 }
 
+inline hashval_t
+alias_hasher::hash (alias_hasher::compare_type tree)
+{
+  return htab_hash_pointer (tree);
+}
+
+inline bool
+alias_hasher::equal (const value_type type, compare_type tree)
+{
+  return type->tree == tree;
+}
+
+/* We've encountered a type definition - add it to the type list. */
+static void
+pdbout_type_decl (tree t, int local ATTRIBUTE_UNUSED)
+{
+  /* We need to record the typedefs to ensure e.g. that Windows'
+   * LPWSTR gets mapped to wchar_t* rather than uint16_t*.
+   * There is a LF_ALIAS / lfAlias in Microsoft's header files, but
+   * it seems to have been forgotten about - MSVC won't generate it. */
+
+  if (DECL_ORIGINAL_TYPE (t))  // typedef
+{
+  struct pdb_alias *a, **slot;
+
+  a = (struct pdb_alias *) xmalloc (sizeof (struct pdb_alias));
+
+  a->next = aliases;
+  a->tree = TREE_TYPE (t);
+  a->type = find_type (DECL_ORIGINAL_TYPE (t));
+
+  // HRESULTs have their own value
+  if (a->type == long_type && DECL_NAME (t)
+ && IDENTIFIER_POINTER (DECL_NAME (t))
+ && !strcmp (IDENTIFIER_POINTER (DECL_NAME (t)), "HRESULT"))
+   a->type = hresult_type;
+
+  slot =
+   alias_hash_table.find_slot_with_hash (TREE_TYPE (t),
+ htab_hash_pointer (TREE_TYPE
+(t)),
+ INSERT);
+  *slot = a;
+
+  aliases = a;
+
+  return;
+}
+
+  find_type (TREE_TYPE (t));
+}
+
 #ifndef _WIN32
 /* Given a Unix-style path, construct a fake Windows path, which is what windbg
  * and Visual Studio are expecting. This maps / to Z:\, which is the default
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 412378a63ac..3e5ef8ca1a7 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -201,6 +201,13 @@ struct pdb_type
   uint8_t data[1];
 };
 
+struct pdb_alias
+{
+  struct pdb_alias *next;
+  tree_node *tree;
+  struct pdb_type *type;
+};
+
 #define CV_BUILTIN_TYPE_VOID   0x0003
 #define CV_BUILTIN_TYPE_HRESULT0x0008
 #define CV_BUILTIN_TYPE_SIGNED_CHARACTER   0x0010
@@ -122

[PATCH 11/24] pdbout: Output array types.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 133 +++
 gcc/pdbout.h |  15 ++
 2 files changed, 148 insertions(+)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index fa3b1fb0312..8376b0e762c 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -79,6 +79,7 @@ static struct pdb_type *arglist_types = NULL;
 static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
 static struct pdb_type *modifier_types = NULL;
+static struct pdb_type *array_types = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
 static unsigned int num_line_number_entries = 0;
@@ -816,6 +817,71 @@ write_pointer (struct pdb_pointer *ptr)
   fprintf (asm_out_file, "\t.long\t0x%x\n", ptr->attr.num);
 }
 
+/* Output a lfArray structure. */
+static void
+write_array (struct pdb_array *arr)
+{
+  uint16_t len = 15, align;
+
+  if (arr->length >= 0x8000)
+{
+  if (arr->length <= 0x)
+   len += 2;   // LF_USHORT
+  else if (arr->length <= 0x)
+   len += 4;   // LF_ULONG
+  else
+   len += 8;   // LF_UQUADWORD
+}
+
+  align = 4 - (len % 4);
+
+  if (align != 4)
+len += align;
+
+  fprintf (asm_out_file, "\t.short\t0x%lx\n", len - sizeof (uint16_t));
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_ARRAY);
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n", arr->type ? arr->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  arr->index_type ? arr->index_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+
+  if (arr->length >= 0x8000)
+{
+  if (arr->length <= 0x)
+   {
+ fprintf (asm_out_file, "\t.short\t0x%x\n", LF_USHORT);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", (uint16_t) arr->length);
+   }
+  else if (arr->length <= 0x)
+   {
+ fprintf (asm_out_file, "\t.short\t0x%x\n", LF_ULONG);
+ fprintf (asm_out_file, "\t.long\t0x%x\n", (uint32_t) arr->length);
+   }
+  else
+   {
+ fprintf (asm_out_file, "\t.short\t0x%x\n", LF_UQUADWORD);
+ fprintf (asm_out_file, "\t.quad\t0x%" PRIx64 "\n", arr->length);
+   }
+}
+  else
+fprintf (asm_out_file, "\t.short\t0x%x\n", (uint32_t) arr->length);
+
+  fprintf (asm_out_file, "\t.byte\t0\n");  // empty string
+
+  if (align != 4)
+{
+  if (align == 3)
+   fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+  if (align >= 2)
+   fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+  fprintf (asm_out_file, "\t.byte\t0xf1\n");
+}
+}
+
 /* Output a lfArgList structure, describing the arguments that a
  * procedure expects. */
 static void
@@ -890,6 +956,10 @@ write_type (struct pdb_type *t)
   write_pointer ((struct pdb_pointer *) t->data);
   break;
 
+case LF_ARRAY:
+  write_array ((struct pdb_array *) t->data);
+  break;
+
 case LF_ARGLIST:
   write_arglist ((struct pdb_arglist *) t->data);
   break;
@@ -1074,6 +1144,66 @@ pdbout_late_global_decl (tree var)
   global_vars = v;
 }
 
+/* Given an array type t, allocate a new pdb_type and add it to the
+ * type list. */
+static struct pdb_type *
+find_type_array (tree t)
+{
+  struct pdb_type *arrtype, *last_entry = NULL, *type;
+  struct pdb_array *arr;
+  uint64_t length =
+TYPE_SIZE (t) ? (TREE_INT_CST_ELT (TYPE_SIZE (t), 0) / 8) : 0;
+  struct pdb_type **slot;
+
+  type = find_type (TREE_TYPE (t));
+
+  if (!type)
+return NULL;
+
+  arrtype = array_types;
+  while (arrtype)
+{
+  arr = (struct pdb_array *) arrtype->data;
+
+  if (arr->type == type && arr->length == length)
+   return arrtype;
+
+  last_entry = arrtype;
+  arrtype = arrtype->next2;
+}
+
+  arrtype =
+(struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+sizeof (struct pdb_array));
+  arrtype->cv_type = LF_ARRAY;
+  arrtype->tree = t;
+  arrtype->next = arrtype->next2 = NULL;
+  arrtype->id = 0;
+
+  arr = (struct pdb_array *) arrtype->data;
+  arr->type = type;
+  arr->index_type = ulong_type;
+  arr->length = length;
+
+  if (last_entry)
+last_entry->next2 = arrtype;
+  else
+array_types = arrtype;
+
+  if (last_type)
+last_type->next = arrtype;
+  else
+types = arrtype;
+
+  last_type = arrtype;
+
+  slot =
+tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t), INSERT);
+  *slot = arrtype;
+
+  return arrtype;
+}
+
 /* Add an argument list type. */
 static pdb_type *
 add_arglist_type (struct pdb_type *t)
@@ -1682,6 +1812,9 @@ find_type (tree t)
 case REFERENCE_TYPE:
   return find_type_pointer (t);
 
+case ARRAY_TYPE:
+  return find_type_array (t);
+
 case FUNCTION_TYPE:
 case METHOD_TYPE:
   return find_type_function (t);
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 1fa2b1ab2fa..412378a63

[PATCH 13/24] pdbout: Output information about enums.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 449 +++
 gcc/pdbout.h |  43 +
 2 files changed, 492 insertions(+)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 5089203e339..9701aaf8902 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -46,6 +46,8 @@
 
 #define FIRST_TYPE_NUM 0x1000
 
+static const char unnamed[] = "";
+
 static void pdbout_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
   unsigned int column ATTRIBUTE_UNUSED,
   const char *file ATTRIBUTE_UNUSED);
@@ -80,6 +82,8 @@ static struct pdb_type *arglist_types = NULL;
 static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
 static struct pdb_type *modifier_types = NULL;
+static struct pdb_type *fieldlist_types = NULL;
+static struct pdb_type *enum_types = NULL;
 static struct pdb_type *array_types = NULL;
 static struct pdb_alias *aliases = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
@@ -806,9 +810,235 @@ write_pdb_section (void)
 static void
 free_type (struct pdb_type *t)
 {
+  switch (t->cv_type)
+{
+case LF_FIELDLIST:
+  {
+   struct pdb_fieldlist *fl = (struct pdb_fieldlist *) t->data;
+
+   for (unsigned int i = 0; i < fl->count; i++)
+ {
+   if (fl->entries[i].name)
+ free (fl->entries[i].name);
+ }
+
+   break;
+  }
+
+case LF_ENUM:
+  {
+   struct pdb_enum *en = (struct pdb_enum *) t->data;
+
+   if (en->name)
+ free (en->name);
+
+   break;
+  }
+}
+
   free (t);
 }
 
+/* Output a lfFieldlist structure, which describes the values of an enum. */
+static void
+write_fieldlist (struct pdb_fieldlist *fl)
+{
+  unsigned int len = 4;
+
+  for (unsigned int i = 0; i < fl->count; i++)
+{
+  len += 2;
+
+  switch (fl->entries[i].cv_type)
+   {
+   case LF_ENUMERATE:
+ len += 5;
+
+ /* Positive values less than 0x8000 are stored as they are; otherwise
+  * we prepend two bytes describing what type it is. */
+
+ if (fl->entries[i].value >= 0x8000 || fl->entries[i].value < 0)
+   {
+ if (fl->entries[i].value >= -127 && fl->entries[i].value < 0)
+   len++;  // LF_CHAR
+ else if (fl->entries[i].value >= -0x7fff &&
+  fl->entries[i].value <= 0x7fff)
+   {
+ len += 2; // LF_SHORT
+   }
+ else if (fl->entries[i].value >= 0x8000 &&
+  fl->entries[i].value <= 0x)
+   {
+ len += 2; // LF_USHORT
+   }
+ else if (fl->entries[i].value >= -0x7fff &&
+  fl->entries[i].value <= 0x7fff)
+   {
+ len += 4; // LF_LONG
+   }
+ else if (fl->entries[i].value >= 0x8000 &&
+  fl->entries[i].value <= 0x)
+   {
+ len += 4; // LF_ULONG
+   }
+ else
+   len += 8;   // LF_QUADWORD or LF_UQUADWORD
+   }
+
+ if (fl->entries[i].name)
+   len += strlen (fl->entries[i].name);
+
+ break;
+   }
+
+  if (len % 4 != 0)
+   len += 4 - (len % 4);
+}
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_FIELDLIST);
+
+  for (unsigned int i = 0; i < fl->count; i++)
+{
+  fprintf (asm_out_file, "\t.short\t0x%x\n", fl->entries[i].cv_type);
+
+  switch (fl->entries[i].cv_type)
+   {
+   case LF_ENUMERATE:
+ {
+   size_t name_len =
+ fl->entries[i].name ? strlen (fl->entries[i].name) : 0;
+   unsigned int align;
+
+   fprintf (asm_out_file, "\t.short\t0x%x\n",
+fl->entries[i].fld_attr);
+
+   align = (3 + name_len) % 4;
+
+   if (fl->entries[i].value >= 0 && fl->entries[i].value < 0x8000)
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+ (uint16_t) fl->entries[i].value);
+   else if (fl->entries[i].value >= -127 && fl->entries[i].value < 0)
+ {
+   fprintf (asm_out_file, "\t.short\t0x%x\n", LF_CHAR);
+   fprintf (asm_out_file, "\t.byte\t0x%x\n",
+   (unsigned int) ((int8_t) fl->entries[i].value & 0xff));
+
+   align = (align + 1) % 4;
+ }
+   else if (fl->entries[i].value >= -0x7fff
+   && fl->entries[i].value <= 0x7fff)
+ {
+   fprintf (asm_out_file, "\t.short\t0x%x\n", LF_SHORT);
+   fprintf (asm_out_file, "\t.short\t0x%x\n",
+   (unsigned int) ((int16_t) fl->entries[i].
+   value & 0x));
+
+   align = (align + 2) % 4;
+ }
+   

[PATCH 15/24] pdbout: Output definitions of unions.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 62 +++-
 gcc/pdbout.h |  1 +
 2 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 3bfec519877..7d493513e06 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -829,6 +829,7 @@ free_type (struct pdb_type *t)
 
 case LF_CLASS:
 case LF_STRUCTURE:
+case LF_UNION:
   {
struct pdb_struct *str = (struct pdb_struct *) t->data;
 
@@ -1097,6 +1098,44 @@ write_struct (uint16_t type, struct pdb_struct *str)
 }
 }
 
+/* Output a lfUnion structure. */
+static void
+write_union (struct pdb_struct *str)
+{
+  size_t name_len = str->name ? strlen (str->name) : (sizeof (unnamed) - 1);
+  unsigned int len = 15 + name_len, align;
+
+  if (len % 4 != 0)
+len += 4 - (len % 4);
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_UNION);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", str->count);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", str->property.value);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  str->field_type ? str->field_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", str->size);
+
+  if (str->name)
+ASM_OUTPUT_ASCII (asm_out_file, str->name, name_len + 1);
+  else
+ASM_OUTPUT_ASCII (asm_out_file, unnamed, sizeof (unnamed));
+
+  align = 4 - ((3 + name_len) % 4);
+
+  if (align != 4)
+{
+  if (align == 3)
+   fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+  if (align >= 2)
+   fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+  fprintf (asm_out_file, "\t.byte\t0xf1\n");
+}
+}
+
 /* Output a lfEnum structure. */
 static void
 write_enum (struct pdb_enum *en)
@@ -1288,6 +1327,10 @@ write_type (struct pdb_type *t)
   write_struct (t->cv_type, (struct pdb_struct *) t->data);
   break;
 
+case LF_UNION:
+  write_union ((struct pdb_struct *) t->data);
+  break;
+
 case LF_ENUM:
   write_enum ((struct pdb_enum *) t->data);
   break;
@@ -1679,10 +1722,10 @@ struct_hasher::equal (const value_type type, 
compare_type name)
   return !strcmp (str->name, name);
 }
 
-/* For a given struct or class, allocate a new pdb_type and
+/* For a given struct, class, or union, allocate a new pdb_type and
  * add it to the type list. */
 static struct pdb_type *
-find_type_struct (tree t)
+find_type_struct (tree t, bool is_union)
 {
   tree f;
   struct pdb_type *fltype = NULL, *strtype, *fwddef = NULL,
@@ -1729,7 +1772,8 @@ find_type_struct (tree t)
 
  if (type
  && (type->cv_type == LF_CLASS
- || type->cv_type == LF_STRUCTURE))
+ || type->cv_type == LF_STRUCTURE
+ || type->cv_type == LF_UNION))
{
  struct pdb_struct *str2 = (struct pdb_struct *) type->data;
 
@@ -1811,7 +1855,8 @@ find_type_struct (tree t)
 
  if (type
  && (type->cv_type == LF_CLASS
- || type->cv_type == LF_STRUCTURE))
+ || type->cv_type == LF_STRUCTURE
+ || type->cv_type == LF_UNION))
{
  struct pdb_struct *str2 =
(struct pdb_struct *) type->data;
@@ -1880,7 +1925,9 @@ find_type_struct (tree t)
 (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
 sizeof (struct pdb_struct));
 
-  if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
+  if (is_union)
+strtype->cv_type = LF_UNION;
+  else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
 strtype->cv_type = LF_CLASS;
   else
 strtype->cv_type = LF_STRUCTURE;
@@ -2744,7 +2791,10 @@ find_type (tree t)
   return find_type_array (t);
 
 case RECORD_TYPE:
-  return find_type_struct (t);
+  return find_type_struct (t, false);
+
+case UNION_TYPE:
+  return find_type_struct (t, true);
 
 case ENUMERAL_TYPE:
   return find_type_enum (t);
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 96288d235ea..09b3914d650 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -41,6 +41,7 @@
 #define LF_ARRAY   0x1503
 #define LF_CLASS   0x1504
 #define LF_STRUCTURE   0x1505
+#define LF_UNION   0x1506
 #define LF_ENUM0x1507
 #define LF_MEMBER  0x150d
 #define LF_CHAR0x8000
-- 
2.26.2



[PATCH 14/24] pdbout: Output definitions of structs and classes.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 469 ++-
 gcc/pdbout.h |  51 ++
 2 files changed, 517 insertions(+), 3 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 9701aaf8902..3bfec519877 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -83,6 +83,7 @@ static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
 static struct pdb_type *modifier_types = NULL;
 static struct pdb_type *fieldlist_types = NULL;
+static struct pdb_type *struct_types = NULL, *last_struct_type = NULL;
 static struct pdb_type *enum_types = NULL;
 static struct pdb_type *array_types = NULL;
 static struct pdb_alias *aliases = NULL;
@@ -105,6 +106,7 @@ static struct pdb_type *complex16_type, *complex32_type, 
*complex48_type,
 static struct pdb_type *void_type, *nullptr_type;
 static bool builtins_initialized = false;
 static hash_table  alias_hash_table (31);
+static hash_table  struct_hash_table (31);
 
 const struct gcc_debug_hooks pdb_debug_hooks = {
   pdbout_init,
@@ -825,6 +827,17 @@ free_type (struct pdb_type *t)
break;
   }
 
+case LF_CLASS:
+case LF_STRUCTURE:
+  {
+   struct pdb_struct *str = (struct pdb_struct *) t->data;
+
+   if (str->name)
+ free (str->name);
+
+   break;
+  }
+
 case LF_ENUM:
   {
struct pdb_enum *en = (struct pdb_enum *) t->data;
@@ -839,7 +852,8 @@ free_type (struct pdb_type *t)
   free (t);
 }
 
-/* Output a lfFieldlist structure, which describes the values of an enum. */
+/* Output a lfFieldlist structure, which describes the fields of a struct,
+ * class, or union, or the values of an enum. */
 static void
 write_fieldlist (struct pdb_fieldlist *fl)
 {
@@ -851,6 +865,10 @@ write_fieldlist (struct pdb_fieldlist *fl)
 
   switch (fl->entries[i].cv_type)
{
+   case LF_MEMBER:
+ len += 9 + (fl->entries[i].name ? strlen (fl->entries[i].name) : 0);
+ break;
+
case LF_ENUMERATE:
  len += 5;
 
@@ -904,6 +922,43 @@ write_fieldlist (struct pdb_fieldlist *fl)
 
   switch (fl->entries[i].cv_type)
{
+   case LF_MEMBER:
+ {
+   size_t name_len =
+ fl->entries[i].name ? strlen (fl->entries[i].name) : 0;
+   unsigned int align;
+
+   fprintf (asm_out_file, "\t.short\t0x%x\n",
+fl->entries[i].fld_attr);
+   fprintf (asm_out_file, "\t.short\t0x%x\n",
+   fl->entries[i].type ? fl->entries[i].type->id : 0);
+   fprintf (asm_out_file, "\t.short\t0\n");// padding
+   fprintf (asm_out_file, "\t.short\t0x%x\n", fl->entries[i].offset);
+
+   if (fl->entries[i].name)
+ ASM_OUTPUT_ASCII (asm_out_file, fl->entries[i].name,
+   name_len + 1);
+   else
+ fprintf (asm_out_file, "\t.byte\t0\n");
+
+   // handle alignment padding
+
+   align = 4 - ((3 + name_len) % 4);
+
+   if (align != 4)
+ {
+   if (align == 3)
+ fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+   if (align >= 2)
+ fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+   fprintf (asm_out_file, "\t.byte\t0xf1\n");
+ }
+
+   break;
+ }
+
case LF_ENUMERATE:
  {
size_t name_len =
@@ -1000,6 +1055,48 @@ write_fieldlist (struct pdb_fieldlist *fl)
 }
 }
 
+/* Output a lfClass / lfStructure struct. */
+static void
+write_struct (uint16_t type, struct pdb_struct *str)
+{
+  size_t name_len = str->name ? strlen (str->name) : (sizeof (unnamed) - 1);
+  unsigned int len = 23 + name_len, align;
+
+  if (len % 4 != 0)
+len += 4 - (len % 4);
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", type);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", str->count);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", str->property.value);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  str->field_type ? str->field_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // derived
+  fprintf (asm_out_file, "\t.short\t0\n"); // vshape
+  fprintf (asm_out_file, "\t.short\t0\n");
+  fprintf (asm_out_file, "\t.short\t0\n");
+  fprintf (asm_out_file, "\t.short\t0\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", str->size);
+
+  if (str->name)
+ASM_OUTPUT_ASCII (asm_out_file, str->name, name_len + 1);
+  else
+ASM_OUTPUT_ASCII (asm_out_file, unnamed, sizeof (unnamed));
+
+  align = 4 - ((3 + name_len) % 4);
+
+  if (align != 4)
+{
+  if (align == 3)
+   fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+  if (align >= 2)
+   fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+  fprintf (asm_out_file, "\t.byte\t0xf1\n");
+}
+}
+
 /* Output a lfEnum structure. */
 static void
 write_enum (struct pdb_enum *en)
@@ -1186,6 +1283,11 @@ write_type (struct pdb_type *t)
   write_field

[PATCH 16/24] pdbout: Output definitions of bitfields within structs.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 93 ++--
 gcc/pdbout.h |  8 +
 2 files changed, 99 insertions(+), 2 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 7d493513e06..d3f251f22d2 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -85,6 +85,7 @@ static struct pdb_type *modifier_types = NULL;
 static struct pdb_type *fieldlist_types = NULL;
 static struct pdb_type *struct_types = NULL, *last_struct_type = NULL;
 static struct pdb_type *enum_types = NULL;
+static struct pdb_type *bitfield_types = NULL;
 static struct pdb_type *array_types = NULL;
 static struct pdb_alias *aliases = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
@@ -1312,6 +1313,22 @@ write_modifier (struct pdb_modifier *t)
   fprintf (asm_out_file, "\t.short\t0\n"); // padding
 }
 
+/* Output lfBitfield structure. */
+static void
+write_bitfield (struct pdb_bitfield *t)
+{
+  fprintf (asm_out_file, "\t.short\t0xa\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_BITFIELD);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  t->underlying_type ? t->underlying_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", t->size);
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", t->offset);
+
+  fprintf (asm_out_file, "\t.byte\t0xf2\n");   // alignment
+  fprintf (asm_out_file, "\t.byte\t0xf1\n");   // alignment
+}
+
 /* Given a pdb_type, output its definition. */
 static void
 write_type (struct pdb_type *t)
@@ -1357,6 +1374,10 @@ write_type (struct pdb_type *t)
 case LF_MODIFIER:
   write_modifier ((struct pdb_modifier *) t->data);
   break;
+
+case LF_BITFIELD:
+  write_bitfield ((struct pdb_bitfield *) t->data);
+  break;
 }
 }
 
@@ -1548,6 +1569,57 @@ pdbout_late_global_decl (tree var)
   global_vars = v;
 }
 
+/* Allocate a new pdb_type for a bitfield. */
+static struct pdb_type *
+find_type_bitfield (struct pdb_type *underlying_type, unsigned int size,
+   unsigned int offset)
+{
+  struct pdb_type *type, *last_entry = NULL;
+  struct pdb_bitfield *bf;
+
+  type = bitfield_types;
+  while (type)
+{
+  bf = (struct pdb_bitfield *) type->data;
+
+  if (bf->underlying_type == underlying_type && bf->size == size
+ && bf->offset == offset)
+   return type;
+
+  last_entry = type;
+  type = type->next2;
+}
+
+  type =
+(struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+sizeof (struct pdb_bitfield));
+
+  type->cv_type = LF_BITFIELD;
+  type->tree = NULL;
+  type->next = type->next2 = NULL;
+  type->id = 0;
+
+  bf = (struct pdb_bitfield *) type->data;
+
+  bf->underlying_type = underlying_type;
+  bf->size = size;
+  bf->offset = offset;
+
+  if (last_entry)
+last_entry->next2 = type;
+  else
+bitfield_types = type;
+
+  if (last_type)
+last_type->next = type;
+  else
+types = type;
+
+  last_type = type;
+
+  return type;
+}
+
 /* Allocate a pdb_type for a forward declaration for a struct. The debugger
  * will resolve this automatically, by searching for a substantive
  * struct definition with the same name. */
@@ -1844,8 +1916,25 @@ find_type_struct (tree t, bool is_union)
  ent->fld_attr = CV_FLDATTR_PUBLIC;
  ent->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (f)));
 
- ent->type = find_type (TREE_TYPE (f));
- ent->offset = bit_offset / 8;
+ if (DECL_BIT_FIELD_TYPE (f))
+   {
+ struct pdb_type *underlying_type =
+   find_type (DECL_BIT_FIELD_TYPE (f));
+
+ ent->type =
+   find_type_bitfield (underlying_type,
+   TREE_INT_CST_ELT (DECL_SIZE (f),
+ 0),
+   TREE_INT_CST_ELT
+   (DECL_FIELD_BIT_OFFSET (f), 0));
+ ent->offset =
+   TREE_INT_CST_ELT (DECL_FIELD_OFFSET (f), 0);
+   }
+ else
+   {
+ ent->type = find_type (TREE_TYPE (f));
+ ent->offset = bit_offset / 8;
+   }
 
  ent++;
}
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 09b3914d650..e12f1cf21a0 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -37,6 +37,7 @@
 #define S_DEFRANGE_REGISTER_REL0x1145
 #define LF_ARGLIST 0x1201
 #define LF_FIELDLIST   0x1203
+#define LF_BITFIELD0x1205
 #define LF_ENUMERATE   0x1502
 #define LF_ARRAY   0x1503
 #define LF_CLASS   0x1504
@@ -373,6 +374,13 @@ struct pdb_modifier
   uint16_t modifier;
 };
 
+struct pdb_bitfie

[PATCH 17/24] pdbout: Prepend namespaces to struct and function names.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 93 ++--
 1 file changed, 91 insertions(+), 2 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index d3f251f22d2..fb40f066bd9 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -1484,24 +1484,113 @@ pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
   write_pdb_type_section ();
 }
 
-/* For a tree t, construct the name. */
+/* For a tree t, construct the name - namespaces, plus the
+ * base name of the tree. */
 static char *
 get_tree_name (tree t)
 {
   char *name;
+  tree ns;
+
+  static const char anon_ns[] = "";
 
   if (TREE_CODE (t) == FUNCTION_DECL)
 name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
   else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE)
 name = xstrdup (IDENTIFIER_POINTER (TYPE_NAME (t)));
   else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == TYPE_DECL
-&& IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)))[0] != '.')
+  && IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)))[0] != '.')
 name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t;
   else if (DECL_NAME (t) && TREE_CODE (DECL_NAME (t)) == IDENTIFIER_NODE)
 name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
   else
 return NULL;
 
+  /* Prepend any namespaces, if present */
+
+  if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == TYPE_DECL)
+ns = DECL_CONTEXT (TYPE_NAME (t));
+  else if (DECL_NAME (t))
+ns = DECL_CONTEXT (t);
+  else
+ns = NULL;
+
+  if (ns)
+{
+  if (TREE_CODE (ns) == NAMESPACE_DECL)
+   {
+ tree orig_ns = ns;
+ size_t ns_len = 0;
+
+ while (ns && TREE_CODE (ns) == NAMESPACE_DECL)
+   {
+ if (DECL_NAME (ns))
+   ns_len += strlen (IDENTIFIER_POINTER (DECL_NAME (ns))) + 2;
+ else
+   ns_len += sizeof (anon_ns) - 1 + 2;
+
+ ns = DECL_CONTEXT (ns);
+   }
+
+ if (ns_len > 0)
+   {
+ char *tmp, *s;
+ size_t name_len = strlen (name);
+
+ tmp = (char *) xmalloc (name_len + ns_len + 1);
+ memcpy (&tmp[ns_len], name, name_len + 1);
+ free (name);
+ name = tmp;
+
+ ns = orig_ns;
+ s = &name[ns_len];
+
+ while (ns && TREE_CODE (ns) == NAMESPACE_DECL)
+   {
+ size_t len;
+
+ s -= 2;
+ memcpy (s, "::", 2);
+
+ if (DECL_NAME (ns))
+   {
+ len = strlen (IDENTIFIER_POINTER (DECL_NAME (ns)));
+ s -= len;
+ memcpy (s, IDENTIFIER_POINTER (DECL_NAME (ns)), len);
+   }
+ else
+   {
+ s -= sizeof (anon_ns) - 1;
+ memcpy (s, anon_ns, sizeof (anon_ns) - 1);
+   }
+
+ ns = DECL_CONTEXT (ns);
+   }
+   }
+   }
+  else if (TREE_CODE (ns) == RECORD_TYPE
+  || TREE_CODE (ns) == FUNCTION_DECL)
+   {
+ char *s = get_tree_name (ns);
+ char *tmp;
+ size_t name_len = strlen (name);
+ size_t s_len = s ? strlen (s) : 1;
+
+ tmp = (char *) xmalloc (name_len + s_len + 3);
+ memcpy (&tmp[s_len + 2], name, name_len + 1);
+ free (name);
+ name = tmp;
+
+ if (s)
+   memcpy (name, s, s_len);
+ else
+   name[0] = '?';
+
+ name[s_len] = ':';
+ name[s_len + 1] = ':';
+   }
+}
+
   return name;
 }
 
-- 
2.26.2



[PATCH 18/24] pdbout: Append template information to struct or function name.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 346 ++-
 1 file changed, 344 insertions(+), 2 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index fb40f066bd9..0cae4d33469 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -73,6 +73,7 @@ static void pdbout_end_block (unsigned int line 
ATTRIBUTE_UNUSED,
  unsigned int blocknum);
 
 static struct pdb_type *find_type (tree t);
+static char *get_tree_name (tree t);
 
 static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_block *cur_block = NULL;
@@ -1484,13 +1485,264 @@ pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
   write_pdb_type_section ();
 }
 
+/* Reallocate the string n, adding the type name of arg and the character
+ * suffix.
+ * We can't use the C++ pretty printer for this as this file gets
+ * compiled into libbackend.a. */
+static void
+append_template_element (char **n, size_t *len, tree arg, char suffix)
+{
+  char *tmp;
+  char *name = *n;
+
+  switch (TREE_CODE (arg))
+{
+case RECORD_TYPE:
+case UNION_TYPE:
+  {
+   char *s = get_tree_name (arg);
+
+   if (s)
+ {
+   size_t s_len = strlen (s);
+
+   tmp = (char *) xmalloc (*len + s_len + 2);
+   memcpy (tmp, name, *len);
+   free (name);
+   name = tmp;
+
+   memcpy (&name[*len], s, s_len);
+   name[*len + s_len] = suffix;
+   name[*len + s_len + 1] = 0;
+   *len += s_len + 1;
+
+   free (s);
+ }
+   else
+ {
+   tmp = (char *) xmalloc (*len + 3);
+   memcpy (tmp, name, *len);
+   free (name);
+   name = tmp;
+
+   name[*len] = '?';
+   name[*len + 1] = suffix;
+   name[*len + 2] = 0;
+   *len += 2;
+ }
+
+   break;
+  }
+
+case INTEGER_TYPE:
+case BOOLEAN_TYPE:
+case REAL_TYPE:
+case VOID_TYPE:
+case NULLPTR_TYPE:
+case ENUMERAL_TYPE:
+  {
+   const char *s;
+   size_t s_len;
+
+   if (TREE_CODE (arg) == NULLPTR_TYPE)
+ s = "std::nullptr_t";
+   else
+ s = IDENTIFIER_POINTER (TYPE_IDENTIFIER (arg));
+
+   s_len = strlen (s);
+
+   tmp = (char *) xmalloc (*len + s_len + 2);
+   memcpy (tmp, name, *len);
+   free (name);
+   name = tmp;
+
+   memcpy (&name[*len], s, s_len);
+   name[*len + s_len] = suffix;
+   name[*len + s_len + 1] = 0;
+   *len += s_len + 1;
+
+   break;
+  }
+
+case POINTER_TYPE:
+  {
+   append_template_element (&name, len, TREE_TYPE (arg), '*');
+
+   tmp = (char *) xmalloc (*len + 2);
+   memcpy (tmp, name, *len);
+   free (name);
+   name = tmp;
+
+   name[*len] = suffix;
+   name[*len + 1] = 0;
+   (*len)++;
+
+   break;
+  }
+
+case INTEGER_CST:
+  if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE)
+   {
+ if (TREE_INT_CST_ELT_CHECK (arg, 0) == 0)
+   {
+ static const char str[] = "false";
+
+ tmp = (char *) xmalloc (*len + sizeof (str) + 2);
+ memcpy (tmp, name, *len);
+ free (name);
+ name = tmp;
+
+ memcpy (&name[*len], str, sizeof (str) - 1);
+ name[*len + sizeof (str) - 1] = suffix;
+ name[*len + sizeof (str)] = 0;
+ *len += sizeof (str);
+   }
+ else
+   {
+ static const char str[] = "true";
+
+ tmp = (char *) xmalloc (*len + sizeof (str) + 2);
+ memcpy (tmp, name, *len);
+ free (name);
+ name = tmp;
+
+ memcpy (&name[*len], str, sizeof (str) - 1);
+ name[*len + sizeof (str) - 1] = suffix;
+ name[*len + sizeof (str)] = 0;
+ *len += sizeof (str);
+   }
+   }
+  else
+   {
+ char s[50];
+ size_t s_len;
+
+ if (TYPE_UNSIGNED (arg))
+   sprintf (s, "%lu", TREE_INT_CST_ELT_CHECK (arg, 0));
+ else
+   sprintf (s, "%li", TREE_INT_CST_ELT_CHECK (arg, 0));
+
+ s_len = strlen (s);
+
+ tmp = (char *) xmalloc (*len + s_len + 2);
+ memcpy (tmp, name, *len);
+ free (name);
+ name = tmp;
+
+ memcpy (&name[*len], s, s_len);
+ name[*len + s_len] = suffix;
+ name[*len + s_len + 1] = 0;
+ *len += s_len + 1;
+   }
+  break;
+
+case REFERENCE_TYPE:
+  {
+   append_template_element (&name, len, TREE_TYPE (arg), '&');
+
+   tmp = (char *) xmalloc (*len + 2);
+   memcpy (tmp, name, *len);
+   free (name);
+   name = tmp;
+
+   name[*len] = suffix;
+   name[*len + 1] = 0;
+   (*len)++;
+
+   break;
+  }
+
+case TYPE_ARGUMENT_PACK:
+  {
+   static const char str[] = "...";
+
+   tmp = (char *) xmalloc (*len + sizeof (str) + 2);
+   memcpy (tmp, name, *len);
+   free (name);

[PATCH 19/24] pdbout: Handle typedefs to anonymous types.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 79 +++-
 1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 0cae4d33469..dae5c1ef679 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -3344,6 +3344,8 @@ alias_hasher::equal (const value_type type, compare_type 
tree)
 static void
 pdbout_type_decl (tree t, int local ATTRIBUTE_UNUSED)
 {
+  struct pdb_type *type;
+
   /* We need to record the typedefs to ensure e.g. that Windows'
* LPWSTR gets mapped to wchar_t* rather than uint16_t*.
* There is a LF_ALIAS / lfAlias in Microsoft's header files, but
@@ -3365,6 +3367,47 @@ pdbout_type_decl (tree t, int local ATTRIBUTE_UNUSED)
  && !strcmp (IDENTIFIER_POINTER (DECL_NAME (t)), "HRESULT"))
a->type = hresult_type;
 
+  // give name if previously anonymous
+
+  if (a->type)
+   {
+ switch (a->type->cv_type)
+   {
+   case LF_STRUCTURE:
+   case LF_CLASS:
+   case LF_UNION:
+ {
+   struct pdb_struct *str = (struct pdb_struct *) a->type->data;
+
+   if (!str->name)
+ {
+   struct pdb_type **slot;
+
+   str->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+
+   slot =
+ struct_hash_table.find_slot_with_hash (str->name,
+struct_hasher::
+hash (str->name),
+INSERT);
+   *slot = a->type;
+ }
+
+   break;
+ }
+
+   case LF_ENUM:
+ {
+   struct pdb_enum *en = (struct pdb_enum *) a->type->data;
+
+   if (!en->name)
+ en->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+
+   break;
+ }
+   }
+   }
+
   slot =
alias_hash_table.find_slot_with_hash (TREE_TYPE (t),
  htab_hash_pointer (TREE_TYPE
@@ -3377,7 +3420,41 @@ pdbout_type_decl (tree t, int local ATTRIBUTE_UNUSED)
   return;
 }
 
-  find_type (TREE_TYPE (t));
+  type = find_type (TREE_TYPE (t));
+
+  if (!type || type->id != 0)
+return;
+
+  if (DECL_NAME (t) && IDENTIFIER_POINTER (DECL_NAME (t))
+  && IDENTIFIER_POINTER (DECL_NAME (t))[0] != '.')
+{
+  // give name if previously anonymous
+
+  switch (type->cv_type)
+   {
+   case LF_STRUCTURE:
+   case LF_CLASS:
+   case LF_UNION:
+ {
+   struct pdb_struct *str = (struct pdb_struct *) type->data;
+
+   if (!str->name)
+ str->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+
+   break;
+ }
+
+   case LF_ENUM:
+ {
+   struct pdb_enum *en = (struct pdb_enum *) type->data;
+
+   if (!en->name)
+ en->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+
+   break;
+ }
+   }
+}
 }
 
 #ifndef _WIN32
-- 
2.26.2



[PATCH 20/24] pdbout: Output file and line number of type definitions.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 208 ++-
 gcc/pdbout.h |  10 +++
 2 files changed, 217 insertions(+), 1 deletion(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index dae5c1ef679..8dbd8f58a87 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -79,6 +79,7 @@ static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
+static struct pdb_type *string_types = NULL;
 static struct pdb_type *arglist_types = NULL;
 static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
@@ -1301,6 +1302,53 @@ write_procedure (struct pdb_proc *proc)
   fprintf (asm_out_file, "\t.short\t0\n"); // padding
 }
 
+/* Output lfStringId structure. */
+static void
+write_string_id (struct pdb_type *t)
+{
+  size_t string_len = strlen ((const char *) t->data);
+  size_t len = 9 + string_len, align;
+
+  if (len % 4 != 0)
+align = 4 - (len % 4);
+  else
+align = 0;
+
+  len += align;
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  (uint16_t) (len - sizeof (uint16_t)));
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_STRING_ID);
+  fprintf (asm_out_file, "\t.long\t0\n");
+  ASM_OUTPUT_ASCII (asm_out_file, (const char *) t->data, string_len + 1);
+
+  if (align == 3)
+fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+  if (align >= 2)
+fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+  if (align >= 1)
+fprintf (asm_out_file, "\t.byte\t0xf1\n");
+}
+
+/* Output lfUdtSrcLine structure, describing on which line in a file a
+ * type is defined. The linker transforms this into a lfUdtModSrcLine
+ * structure (LF_UDT_MOD_SRC_LINE), which also adds details of the
+ * "module" (i.e. object file). */
+static void
+write_udt_src_line (struct pdb_udt_src_line *t)
+{
+  fprintf (asm_out_file, "\t.short\t0xe\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_UDT_SRC_LINE);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", t->type ? t->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+  t->source_file ? t->source_file->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n"); // padding
+  fprintf (asm_out_file, "\t.long\t0x%x\n", t->line);
+}
+
 /* Output lfModifier structure, representing a const or volatile version
  * of an existing type. */
 static void
@@ -1372,6 +1420,14 @@ write_type (struct pdb_type *t)
   write_procedure ((struct pdb_proc *) t->data);
   break;
 
+case LF_STRING_ID:
+  write_string_id (t);
+  break;
+
+case LF_UDT_SRC_LINE:
+  write_udt_src_line ((struct pdb_udt_src_line *) t->data);
+  break;
+
 case LF_MODIFIER:
   write_modifier ((struct pdb_modifier *) t->data);
   break;
@@ -2029,6 +2085,7 @@ find_type_bitfield (struct pdb_type *underlying_type, 
unsigned int size,
   type->tree = NULL;
   type->next = type->next2 = NULL;
   type->id = 0;
+  type->udt_src_line = NULL;
 
   bf = (struct pdb_bitfield *) type->data;
 
@@ -2081,6 +2138,7 @@ add_struct_forward_declaration (tree t, const char *name)
   strtype->tree = NULL;
   strtype->next = strtype->next2 = NULL;
   strtype->id = 0;
+  strtype->udt_src_line = NULL;
 
   str = (struct pdb_struct *) strtype->data;
   str->count = 0;
@@ -2195,6 +2253,7 @@ add_type_fieldlist (struct pdb_type *t)
 
   t->next = t->next2 = NULL;
   t->id = 0;
+  t->udt_src_line = NULL;
 
   if (last_entry)
 last_entry->next2 = t;
@@ -2459,6 +2518,7 @@ find_type_struct (tree t, bool is_union)
 
   strtype->next = strtype->next2 = NULL;
   strtype->id = 0;
+  strtype->udt_src_line = NULL;
 
   str = (struct pdb_struct *) strtype->data;
   str->count = num_entries;
@@ -2539,6 +2599,7 @@ find_type_array (tree t)
   arrtype->tree = t;
   arrtype->next = arrtype->next2 = NULL;
   arrtype->id = 0;
+  arrtype->udt_src_line = NULL;
 
   arr = (struct pdb_array *) arrtype->data;
   arr->type = type;
@@ -2607,6 +2668,7 @@ add_arglist_type (struct pdb_type *t)
 
   t->next = NULL;
   t->next2 = NULL;
+  t->udt_src_line = NULL;
   t->id = 0;
 
   if (last_type)
@@ -2725,6 +2787,7 @@ find_type_enum (tree t)
   enumtype->tree = t;
   enumtype->next = enumtype->next2 = NULL;
   enumtype->id = 0;
+  enumtype->udt_src_line = NULL;
 
   en = (struct pdb_enum *) enumtype->data;
   en->count = num_entries;
@@ -2798,6 +2861,7 @@ find_type_pointer (tree t)
   ptrtype->tree = t;
   ptrtype->next = ptrtype->next2 = NULL;
   ptrtype->id = 0;
+  ptrtype->udt_src_line = NULL;
 
   ptr = (struct pdb_pointer *) ptrtype->data;
   ptr->type = type;
@@ -2925,6 +2989,7 @@ find_type_function (tree t)
   proctype->tree = t;
   proctype->next = proctype->next2 = NULL;
   proctype->id = 0;
+  proctype->udt_src_line = NULL;
 
   proc = (struct pdb_proc *) proctype->data;
 
@@ -2990,6 +3055,7 @@ find_type_modifier (tree t)
   type->tree = t;
   type->next = type->next2 = NULL;
   t

[PATCH 21/24] pdbout: Don't output unused types.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 256 ++-
 gcc/pdbout.h |   1 +
 2 files changed, 254 insertions(+), 3 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 8dbd8f58a87..3d15c620db5 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -1438,7 +1438,8 @@ write_type (struct pdb_type *t)
 }
 }
 
-/* Output the .debug$T section, which contains all the types used. */
+/* Output the .debug$T section, which contains all the types used.
+ * Types defined but not used will not be output. */
 static void
 write_pdb_type_section (void)
 {
@@ -1450,7 +1451,8 @@ write_pdb_type_section (void)
   n = types;
   while (n)
 {
-  write_type (n);
+  if (n->used)
+   write_type (n);
 
   n = n->next;
 }
@@ -1476,6 +1478,227 @@ write_pdb_type_section (void)
 }
 }
 
+/* Loop through our list of types. If a type is marked as used but a type
+ * it refers to isn't, marked that type as used too. */
+static void
+mark_referenced_types_used (void)
+{
+  struct pdb_type *t;
+  bool changed;
+
+  do
+{
+  changed = false;
+
+  t = types;
+  while (t)
+   {
+ if (!t->used)
+   {
+ t = t->next;
+ continue;
+   }
+
+ if (t->udt_src_line && !t->udt_src_line->used)
+   {
+ t->udt_src_line->used = true;
+ changed = true;
+   }
+
+ switch (t->cv_type)
+   {
+   case LF_MODIFIER:
+ {
+   struct pdb_modifier *mod = (struct pdb_modifier *) t->data;
+
+   if (mod->type && !mod->type->used)
+ {
+   mod->type->used = true;
+   changed = true;
+ }
+
+   break;
+ }
+
+   case LF_POINTER:
+ {
+   struct pdb_pointer *ptr = (struct pdb_pointer *) t->data;
+
+   if (ptr->type && !ptr->type->used)
+ {
+   ptr->type->used = true;
+   changed = true;
+ }
+
+   break;
+ }
+
+   case LF_PROCEDURE:
+ {
+   struct pdb_proc *proc = (struct pdb_proc *) t->data;
+
+   if (proc->arg_list && !proc->arg_list->used)
+ {
+   proc->arg_list->used = true;
+   changed = true;
+ }
+
+   if (proc->return_type && !proc->return_type->used)
+ {
+   proc->return_type->used = true;
+   changed = true;
+ }
+
+   break;
+ }
+
+   case LF_ARGLIST:
+ {
+   struct pdb_arglist *al = (struct pdb_arglist *) t->data;
+
+   for (unsigned int i = 0; i < al->count; i++)
+ {
+   if (al->args[i] && !al->args[i]->used)
+ {
+   al->args[i]->used = true;
+   changed = true;
+ }
+ }
+
+   break;
+ }
+
+   case LF_FIELDLIST:
+ {
+   struct pdb_fieldlist *fl = (struct pdb_fieldlist *) t->data;
+
+   for (unsigned int i = 0; i < fl->count; i++)
+ {
+   if (fl->entries[i].type && !fl->entries[i].type->used)
+ {
+   fl->entries[i].type->used = true;
+   changed = true;
+ }
+ }
+
+   break;
+ }
+
+   case LF_BITFIELD:
+ {
+   struct pdb_bitfield *bf = (struct pdb_bitfield *) t->data;
+
+   if (bf->underlying_type && !bf->underlying_type->used)
+ {
+   bf->underlying_type->used = true;
+   changed = true;
+ }
+
+   break;
+ }
+
+   case LF_ARRAY:
+ {
+   struct pdb_array *arr = (struct pdb_array *) t->data;
+
+   if (arr->type && !arr->type->used)
+ {
+   arr->type->used = true;
+   changed = true;
+ }
+
+   if (arr->index_type && !arr->index_type->used)
+ {
+   arr->index_type->used = true;
+   changed = true;
+ }
+
+   break;
+ }
+
+   case LF_CLASS:
+   case LF_STRUCTURE:
+   case LF_UNION:
+ {
+   struct pdb_struct *str = (struct pdb_struct *) t->data;
+
+   if (str->field_type && !str->field_type->used)
+ {
+   str->field_type->used = true;
+   changed = true;
+ }
+
+   // forward declarations should propagate usedness
+   // to actual types
+   if (str->property.s

[PATCH 22/24] pdbout: Split large fieldlists when necessary.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 151 ++-
 gcc/pdbout.h |   1 +
 2 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 3d15c620db5..d9ad659cd9a 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -84,7 +84,7 @@ static struct pdb_type *arglist_types = NULL;
 static struct pdb_type *pointer_types = NULL;
 static struct pdb_type *proc_types = NULL;
 static struct pdb_type *modifier_types = NULL;
-static struct pdb_type *fieldlist_types = NULL;
+static struct pdb_type *fieldlist_types = NULL, *last_fieldlist_type = NULL;
 static struct pdb_type *struct_types = NULL, *last_struct_type = NULL;
 static struct pdb_type *enum_types = NULL;
 static struct pdb_type *bitfield_types = NULL;
@@ -911,6 +911,10 @@ write_fieldlist (struct pdb_fieldlist *fl)
len += strlen (fl->entries[i].name);
 
  break;
+
+   case LF_INDEX:
+ len += 6;
+ break;
}
 
   if (len % 4 != 0)
@@ -1055,6 +1059,12 @@ write_fieldlist (struct pdb_fieldlist *fl)
 
break;
  }
+
+   case LF_INDEX:
+ fprintf (asm_out_file, "\t.short\t0\n"); // padding
+ fprintf (asm_out_file, "\t.short\t0x%x\n", fl->entries[i].type->id);
+ fprintf (asm_out_file, "\t.short\t0\n"); // padding
+ break;
}
 }
 }
@@ -1753,6 +1763,141 @@ number_types (void)
 }
 }
 
+/* The maximum length for a type entry is 0x. If we have a fieldlist
+ * which would be more than that we need to split it, adding an LF_INDEX
+ * entry to point to the continuation entry. */
+static void
+split_large_fieldlists ()
+{
+  struct pdb_type *t = types;
+  struct pdb_type *prev = NULL;
+
+  while (t)
+{
+  struct pdb_fieldlist *fl;
+  unsigned int len, max_len;
+  bool made_split = false;
+
+  if (t->cv_type != LF_FIELDLIST || !t->used)
+   {
+ prev = t;
+ t = t->next;
+ continue;
+   }
+
+  fl = (struct pdb_fieldlist *) t->data;
+
+  /* Maximum length of 0x, minus 8 bytes for the LF_INDEX we might
+   * need to add, rounded down to multiple of 4. */
+  max_len = 0xfff4;
+  len = sizeof (uint16_t) + sizeof (uint16_t); // length + LF_FIELDLIST
+
+  for (int i = fl->count - 1; i >= 0; i--) {
+ unsigned int delta;
+
+ delta = 2; // LF_MEMBER, LF_ENUMERATE, or LF_INDEX
+
+ switch (fl->entries[i].cv_type) {
+   case LF_MEMBER:
+ delta += 9;
+ break;
+
+   case LF_ENUMERATE:
+ delta += 5;
+
+ /* Positive values less than 0x8000 are stored as they are;
+  * otherwise we prepend two bytes describing what type it is. */
+
+ if (fl->entries[i].value >= 0x8000 || fl->entries[i].value < 0)
+   {
+ if (fl->entries[i].value >= -127 && fl->entries[i].value < 0)
+   delta++;// LF_CHAR
+ else if (fl->entries[i].value >= -0x7fff &&
+ fl->entries[i].value <= 0x7fff)
+   {
+ delta += 2;   // LF_SHORT
+   }
+ else if (fl->entries[i].value >= 0x8000 &&
+ fl->entries[i].value <= 0x)
+   {
+ delta += 2;   // LF_USHORT
+   }
+ else if (fl->entries[i].value >= -0x7fff &&
+ fl->entries[i].value <= 0x7fff)
+   {
+ delta += 4;   // LF_LONG
+   }
+ else if (fl->entries[i].value >= 0x8000 &&
+ fl->entries[i].value <= 0x)
+   {
+ delta += 4;   // LF_ULONG
+   }
+ else
+   delta += 8; // LF_QUADWORD or LF_UQUADWORD
+   }
+ break;
+
+   case LF_INDEX:
+ delta += 6;
+ break;
+ }
+
+ if (fl->entries[i].name)
+   delta += strlen (fl->entries[i].name);
+
+ if (delta % 4 != 0)
+   delta += 4 - (delta % 4);
+
+ if (len + delta > max_len)
+   {
+ struct pdb_type *t2;
+ struct pdb_fieldlist *fl2;
+ unsigned int num_entries = fl->count - i - 1;
+
+ t2 =
+   (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+ offsetof (struct pdb_fieldlist, entries) +
+ (num_entries * sizeof (struct pdb_fieldlist_entry)));
+ t2->cv_type = LF_FIELDLIST;
+ t2->next = t;
+ t2->tree = NULL;
+ t2->used = true;
+ t2->id = 0;
+
+ if (prev)
+   prev->next = t2;
+ else
+   types = t2;
+
+ fl2 = (struct pdb_fieldlist *) t2->data;
+ fl2->count = num_

[PATCH 24/24] pdbout: Handle functions with parts in cold section.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 226 ++-
 gcc/pdbout.h |   2 +
 2 files changed, 170 insertions(+), 58 deletions(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 0f5315f7f07..b4528fb79e8 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -71,6 +71,7 @@ static void pdbout_begin_block (unsigned int line 
ATTRIBUTE_UNUSED,
unsigned int blocknum);
 static void pdbout_end_block (unsigned int line ATTRIBUTE_UNUSED,
  unsigned int blocknum);
+static void pdbout_new_section (void);
 
 static struct pdb_type *find_type (tree t);
 static char *get_tree_name (tree t);
@@ -145,7 +146,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
   pdbout_var_location,
   debug_nothing_tree,  /* inline_entry */
   debug_nothing_tree,  /* size_function */
-  debug_nothing_void,  /* switch_text_section */
+  pdbout_new_section,
   debug_nothing_tree_tree, /* set_name */
   0,   /* start_end_main_source_file */
   TYPE_SYMTAB_IS_ADDRESS   /* tree_type_symtab_field */
@@ -157,8 +158,8 @@ pdbout_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
   unsigned int column ATTRIBUTE_UNUSED,
   const char *file ATTRIBUTE_UNUSED)
 {
-  fprintf (asm_out_file, FUNC_BEGIN_LABEL "%u:\n",
-  current_function_funcdef_no);
+  fprintf (asm_out_file, FUNC_BEGIN_LABEL "%s%u:\n",
+  in_cold_section_p ? "cold" : "", current_function_funcdef_no);
 }
 
 /* Add label after function end */
@@ -166,7 +167,8 @@ static void
 pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
 const char *file ATTRIBUTE_UNUSED)
 {
-  fprintf (asm_out_file, FUNC_END_LABEL "%u:\n", current_function_funcdef_no);
+  fprintf (asm_out_file, FUNC_END_LABEL "%s%u:\n",
+  in_cold_section_p ? "cold" : "", current_function_funcdef_no);
 }
 
 /* Output DEFRANGESYMREGISTER or DEFRANGESYMREGISTERREL structure, describing
@@ -464,8 +466,8 @@ pdbout_block (struct pdb_block *block, struct pdb_func 
*func)
}
   else
{
- fprintf (asm_out_file, "\t.long\t[.Lcvprocstart%u]-[.debug$S]\n",
-  func->num);
+ fprintf (asm_out_file, "\t.long\t[.Lcvprocstart%s%u]-[.debug$S]\n",
+  func->cold ? "cold" : "", func->num);
}
 
   fprintf (asm_out_file, "\t.long\t[.Lcvblockend%u]-[.debug$S]\n",
@@ -497,61 +499,72 @@ pdbout_block (struct pdb_block *block, struct pdb_func 
*func)
 static void
 pdbout_proc32 (struct pdb_func *func)
 {
-  size_t name_len = func->name ? strlen (func->name) : 0;
-  uint16_t len = 40 + name_len, align;
-
-  // start procedure
+  /* Don't output function definition if it contains no lines. This can happen
+   * if the compiler creates a cold function consisting of just ud2. */
 
-  if (len % 4 != 0)
+  if (func->lines)
 {
-  align = 4 - (len % 4);
-  len += 4 - (len % 4);
-}
-  else
-align = 0;
+  size_t name_len = func->name ? strlen (func->name) : 0;
+  uint16_t len = 40 + name_len, align;
 
-  fprintf (asm_out_file, ".Lcvprocstart%u:\n", func->num);
-  fprintf (asm_out_file, "\t.short\t0x%x\n",
-  (uint16_t) (len - sizeof (uint16_t)));   // reclen
-  fprintf (asm_out_file, "\t.short\t0x%x\n",
-  func->public_flag ? S_GPROC32 : S_LPROC32);
-  fprintf (asm_out_file, "\t.long\t0\n");  // pParent
-  fprintf (asm_out_file, "\t.long\t[.Lcvprocend%u]-[.debug$S]\n",
-  func->num);  // pEnd
-  fprintf (asm_out_file, "\t.long\t0\n");  // pNext
-  fprintf (asm_out_file,
-  "\t.long\t[" FUNC_END_LABEL "%u]-[" FUNC_BEGIN_LABEL "%u]\n",
-  func->num, func->num);   // len
-  fprintf (asm_out_file, "\t.long\t0\n");  // DbgStart
-  fprintf (asm_out_file, "\t.long\t0\n");  // DbgEnd
-  fprintf (asm_out_file, "\t.short\t0x%x\n", func->type ? func->type->id : 0);
-  fprintf (asm_out_file, "\t.short\t0\n"); // padding
 
-  fprintf (asm_out_file, "\t.secrel32\t" FUNC_BEGIN_LABEL "%u\n",
-  func->num);  // offset
-  fprintf (asm_out_file, "\t.secidx\t" FUNC_BEGIN_LABEL "%u\n",
-  func->num);  // section
+  // start procedure
 
-  fprintf (asm_out_file, "\t.byte\t0\n");  // flags
+  if (len % 4 != 0)
+   {
+ align = 4 - (len % 4);
+ len += 4 - (len % 4);
+   }
+  else
+   align = 0;
 
-  if (func->name)
-ASM_OUTPUT_ASCII (asm_out_file, func->name, name_len + 1);
-  else
-fprintf (asm_out_file, "\t.byte\t0\n");
+  fprintf (asm_out_file, ".Lcvprocstart%s%u:\n",
+ func->cold ? "cold" : "", func->num);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+ (uint16_t) (len - sizeof (uint16_t)));// reclen
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+ func->public_flag ? S_GPROC32 : S_LPROC32);
+  fprintf (asm_out_file, "\t.long\t0\n");  // pParent
+  fprintf (asm_out_file, "\t.lo

[PATCH 23/24] pdbout: Handle names of ctor and dtor functions.

2021-03-20 Thread Mark Harmstone
---
 gcc/pdbout.c | 28 +++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index d9ad659cd9a..0f5315f7f07 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -2175,7 +2175,33 @@ get_tree_name (tree t)
   static const char anon_ns[] = "";
 
   if (TREE_CODE (t) == FUNCTION_DECL)
-name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+{
+  if (DECL_CXX_CONSTRUCTOR_P (t) || DECL_CXX_DESTRUCTOR_P (t))
+   {
+ tree ctx = DECL_CONTEXT (t);
+ const char *basename;
+
+ if (!ctx || !TYPE_NAME (ctx)
+ || TREE_CODE (TYPE_NAME (ctx)) != TYPE_DECL)
+   return NULL;
+
+ basename = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (ctx)));
+
+ if (DECL_CXX_CONSTRUCTOR_P (t))
+   name = xstrdup (basename);
+ else
+   {
+ name = (char *) xmalloc (strlen (basename) + 2);
+
+ name[0] = '~';
+ name[1] = 0;
+
+ strcat (name, basename);
+   }
+   }
+  else
+   name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+}
   else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE)
 name = xstrdup (IDENTIFIER_POINTER (TYPE_NAME (t)));
   else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == TYPE_DECL
-- 
2.26.2



Re: [PATCH 01/24] Add -gcodeview debugging option

2021-03-22 Thread Mark Harmstone
Thanks Richard.

> How do CV and DWARF debug differ and is emitting CV debug from a DWARF
> representation possible (I suppose there might even exist offline
> conversion tools?)

I don't know enough about DWARF internals to answer that, but I'd be surprised
if the answer is "no".

Do you mean that the DWARF code would collect the data, and my code would
process it and output the .debug$S and .debug$T sections? So from the point of
view of the linker, everything will be the same?

> I'll see to add some fat commentary above the debug hooks structure (not
> sure if that will help in practice).

This would definitely help. I'd no idea of this before now, and I would have
spotted a DEPRECATED above the struct definition.

Mark



Re: [PATCH v2] Add -gcodeview option

2022-11-20 Thread Mark Harmstone

On 20/11/22 16:43, Jeff Law wrote:


On 10/26/22 21:38, Mark Harmstone wrote:

Changed to double dashes as per
https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604287.html.


What value is there in providing this option now?  IIUC we don't have any of 
the bits yet to actually produce PDB records.   It seems to me like this ought 
to be patch 1/n of a patch to produce PDB debug symbols.


This isn't useless, as ld will create symbols for the mangled names even 
without the .debug$S and .debug$T sections being present.



[PATCH 2/3] Write LF_POINTER CodeView types for pointers to member functions or data

2024-11-02 Thread Mark Harmstone
Translate DW_TAG_ptr_to_member_type DIEs into special extended
LF_POINTER CodeView types.

gcc/
* dwarf2codeview.cc (struct codeview_custom_type): Add new fields to
lf_pointer struct in union.
(write_lf_pointer): Write containing_class and ptr_to_mem_type if
applicable.
(get_type_num_subroutine_type): Write correct containing_class_type if
this is a pointer to a member function.
(get_type_num_ptr_to_member_type): New function.
(get_type_num): Call get_type_num_ptr_to_member_type.
* dwarf2codeview.h (CV_PTR_MODE_MASK, CV_PTR_MODE_PMEM): Define.
(CV_PTR_MODE_PMFUNC, CV_PMTYPE_D_Single, CV_PMTYPE_F_Single): Likewise.
---
 gcc/dwarf2codeview.cc | 103 ++
 gcc/dwarf2codeview.h  |   9 
 2 files changed, 112 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 722a54b9c4e..876057b5a8c 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1270,6 +1270,8 @@ struct codeview_custom_type
 {
   uint32_t base_type;
   uint32_t attributes;
+  uint32_t containing_class;
+  uint16_t ptr_to_mem_type;
 } lf_pointer;
 struct
 {
@@ -3393,6 +3395,10 @@ write_lf_pointer (codeview_custom_type *t)
   uint16_t kind;
   uint32_t base_type;
   uint32_t attributes;
+  (following only if CV_PTR_MODE_PMEM or CV_PTR_MODE_PMFUNC in attributes)
+  uint32_t containing_class;
+  uint16_t ptr_to_mem_type;
+  uint16_t padding;
 } ATTRIBUTE_PACKED;
   */
 
@@ -3414,6 +3420,20 @@ write_lf_pointer (codeview_custom_type *t)
   fprint_whex (asm_out_file, t->lf_pointer.attributes);
   putc ('\n', asm_out_file);
 
+  if ((t->lf_pointer.attributes & CV_PTR_MODE_MASK) == CV_PTR_MODE_PMEM
+  || (t->lf_pointer.attributes & CV_PTR_MODE_MASK) == CV_PTR_MODE_PMFUNC)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_pointer.containing_class);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_pointer.ptr_to_mem_type);
+  putc ('\n', asm_out_file);
+
+  write_cv_padding (2);
+}
+
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
@@ -5886,6 +5906,30 @@ get_type_num_subroutine_type (dw_die_ref type, bool 
in_struct,
   return_type = T_VOID;
 }
 
+  /* Handle pointer to member function.  */
+  if (containing_class_type == 0)
+{
+  dw_die_ref obj_ptr = get_AT_ref (type, DW_AT_object_pointer);
+
+  if (obj_ptr)
+   {
+ dw_die_ref obj_ptr_type = get_AT_ref (obj_ptr, DW_AT_type);
+
+ if (obj_ptr_type
+ && dw_get_die_tag (obj_ptr_type) == DW_TAG_pointer_type)
+   {
+ dw_die_ref cont_class = get_AT_ref (obj_ptr_type, DW_AT_type);
+
+ if (dw_get_die_tag (cont_class) == DW_TAG_const_type)
+   cont_class = get_AT_ref (cont_class, DW_AT_type);
+
+ containing_class_type = get_type_num (cont_class, in_struct,
+   false);
+ this_type = get_type_num (obj_ptr_type, in_struct, false);
+   }
+   }
+}
+
   /* Count the arguments.  */
 
   first_child = dw_get_die_child (type);
@@ -6121,6 +6165,61 @@ get_type_num_array_type (dw_die_ref type, bool in_struct)
   return element_type;
 }
 
+/* Translate a DW_TAG_ptr_to_member_type DIE, that is a pointer to member
+   function or field, into an LF_POINTER record.  */
+
+static uint32_t
+get_type_num_ptr_to_member_type (dw_die_ref type, bool in_struct)
+{
+  uint32_t base_type_num;
+  uint32_t containing_class;
+  dw_die_ref base_type;
+  codeview_custom_type *ct;
+
+  base_type = get_AT_ref (type, DW_AT_type);
+
+  base_type_num = get_type_num (base_type, in_struct, false);
+  if (base_type_num == 0)
+return 0;
+
+  containing_class = get_type_num (get_AT_ref (type, DW_AT_containing_type),
+  in_struct, false);
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_POINTER;
+  ct->lf_pointer.base_type = base_type_num;
+
+  if (TARGET_64BIT)
+{
+  ct->lf_pointer.attributes = CV_PTR_64;
+  ct->lf_pointer.attributes |= 8 << 13;
+}
+  else
+{
+  ct->lf_pointer.attributes = CV_PTR_NEAR32;
+  ct->lf_pointer.attributes |= 4 << 13;
+}
+
+  ct->lf_pointer.containing_class = containing_class;
+
+  if (base_type && dw_get_die_tag (base_type) == DW_TAG_subroutine_type)
+{
+  ct->lf_pointer.attributes |= CV_PTR_MODE_PMFUNC;
+  ct->lf_pointer.ptr_to_mem_type = CV_PMTYPE_F_Single;
+}
+  else
+{
+  ct->lf_pointer.attributes |= CV_PTR_MODE_PMEM;
+  ct->lf_pointer.ptr_to_mem_type = CV_PMTYPE_D_Single;
+}
+
+  add_custom_type (ct);
+
+  return ct->num;
+}
+
 /* Process a DIE representing a type definition, add a CodeView type if
 

[PATCH 3/3] Handle T_HRESULT types in CodeView records

2024-11-02 Thread Mark Harmstone
Follow MSVC in having a special type value, T_HRESULT, for (signed)
longs that have been typedef'd with the name "HRESULT". This is so that
the debugger can display user-friendly constant names when debugging COM
code.

gcc/
* dwarf2codeview.cc (get_type_num_typedef): New function.
(get_type_num): Call get_type_num_typedef.
* dwarf2codeview.h (T_HRESULT): Define.
---
 gcc/dwarf2codeview.cc | 27 ---
 gcc/dwarf2codeview.h  |  1 +
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 876057b5a8c..5e8a4ab39e7 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -6220,6 +6220,29 @@ get_type_num_ptr_to_member_type (dw_die_ref type, bool 
in_struct)
   return ct->num;
 }
 
+/* Return the type number that corresponds to a DW_TAG_typedef DIE: either the
+   type number of the base type, or follow MSVC in having a special value
+   for the HRESULT used by COM.  */
+
+static uint32_t
+get_type_num_typedef (dw_die_ref type, bool in_struct)
+{
+  uint32_t num;
+
+  num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct, false);
+
+  if (num == T_LONG)
+{
+  const char *name = get_AT_string (type, DW_AT_name);
+
+  /* longs typedef'd as "HRESULT" get their own type */
+  if (name && !strcmp (name, "HRESULT"))
+   num = T_HRESULT;
+}
+
+  return num;
+}
+
 /* Process a DIE representing a type definition, add a CodeView type if
necessary, and return its number.  If it's something we can't handle, return
0.  We keep a hash table so that we're not adding the same type multiple
@@ -6254,9 +6277,7 @@ get_type_num (dw_die_ref type, bool in_struct, bool 
no_fwd_ref)
   break;
 
 case DW_TAG_typedef:
-  /* FIXME - signed longs typedef'd as "HRESULT" should get their
-own type (T_HRESULT) */
-  num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct, false);
+  num = get_type_num_typedef (type, in_struct);
   break;
 
 case DW_TAG_pointer_type:
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index c895ef3e04c..52ffc6cfb99 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3.  If not see
 /* Constants for in-built types.  */
 
 #define T_VOID 0x0003
+#define T_HRESULT  0x0008
 #define T_CHAR 0x0010
 #define T_SHORT0x0011
 #define T_LONG 0x0012
-- 
2.45.2



[PATCH 1/3] Write LF_BCLASS records in CodeView struct definitions

2024-11-02 Thread Mark Harmstone
When writing the CodeView type definition for a struct, translate
DW_TAG_inheritance DIEs into LF_BCLASS records, to record which other
structs this one inherits from.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Add LF_BCLASS.
(struct codeview_subtype): Add lf_bclass to union.
(write_cv_padding): Add declaration.
(write_lf_fieldlist): Handle LF_BCLASS records.
(add_struct_inheritance): New function.
(get_type_num_struct): Call add_struct_inheritance.
---
 gcc/dwarf2codeview.cc | 70 +++
 1 file changed, 70 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index d593795b588..722a54b9c4e 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -100,6 +100,7 @@ enum cv_leaf_type {
   LF_FIELDLIST = 0x1203,
   LF_BITFIELD = 0x1205,
   LF_METHODLIST = 0x1206,
+  LF_BCLASS = 0x1400,
   LF_INDEX = 0x1404,
   LF_ENUMERATE = 0x1502,
   LF_ARRAY = 0x1503,
@@ -1242,6 +1243,12 @@ struct codeview_subtype
   uint32_t method_list;
   char *name;
 } lf_method;
+struct
+{
+  uint16_t attributes;
+  uint32_t base_class_type;
+  codeview_integer offset;
+} lf_bclass;
   };
 };
 
@@ -1422,6 +1429,7 @@ static uint32_t get_type_num_subroutine_type (dw_die_ref 
type, bool in_struct,
  uint32_t containing_class_type,
  uint32_t this_type,
  int32_t this_adjustment);
+static void write_cv_padding (size_t padding);
 
 /* Record new line number against the current function.  */
 
@@ -3846,6 +3854,36 @@ write_lf_fieldlist (codeview_custom_type *t)
  free (v->lf_method.name);
  break;
 
+   case LF_BCLASS:
+ /* This is lf_bclass in binutils and lfBClass in Microsoft's
+cvinfo.h:
+
+   struct lf_bclass
+   {
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t base_class_type;
+ uint16_t offset;
+   } ATTRIBUTE_PACKED;
+ */
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_BCLASS);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_bclass.attributes);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (4, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_bclass.base_class_type);
+ putc ('\n', asm_out_file);
+
+ leaf_len = 8 + write_cv_integer (&v->lf_bclass.offset);
+
+ write_cv_padding (4 - (leaf_len % 4));
+ break;
+
default:
  break;
}
@@ -5462,6 +5500,34 @@ add_struct_function (dw_die_ref c, 
hash_table *method_htab,
 }
 }
 
+/* Create a field list subtype that records the base class that a struct
+   inherits from.  */
+
+static void
+add_struct_inheritance (dw_die_ref c, uint16_t accessibility,
+   codeview_subtype **el, size_t *el_len)
+{
+  /* FIXME: if DW_AT_virtuality is DW_VIRTUALITY_virtual this is a virtual
+   base class, and we should be issuing an LF_VBCLASS record
+   instead.  */
+  if (get_AT_unsigned (c, DW_AT_virtuality) == DW_VIRTUALITY_virtual)
+return;
+
+  *el = (codeview_subtype *) xmalloc (sizeof (**el));
+  (*el)->next = NULL;
+  (*el)->kind = LF_BCLASS;
+  (*el)->lf_bclass.attributes = accessibility;
+  (*el)->lf_bclass.base_class_type = get_type_num (get_AT_ref (c, DW_AT_type),
+  true, false);
+  (*el)->lf_bclass.offset.neg = false;
+  (*el)->lf_bclass.offset.num = get_AT_unsigned (c, 
DW_AT_data_member_location);
+
+  *el_len = 10 + cv_integer_len (&(*el)->lf_bclass.offset);
+
+  if (*el_len % 4)
+*el_len += 4 - (*el_len % 4);
+}
+
 /* Create a new LF_MFUNCTION type for a struct function, add it to the
types_htab hash table, and return its type number.  */
 
@@ -5679,6 +5745,10 @@ get_type_num_struct (dw_die_ref type, bool in_struct, 
bool *is_fwd_ref)
add_struct_function (c, method_htab, &el, &el_len);
  break;
 
+   case DW_TAG_inheritance:
+ add_struct_inheritance (c, accessibility, &el, &el_len);
+ break;
+
default:
  break;
}
-- 
2.45.2



[PATCH] Don't skip CodeView data for partially cold functions

2024-11-12 Thread Mark Harmstone
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,
   

[PATCH 3/4] Write S_INLINEELINES CodeView subsection

2024-11-06 Thread Mark Harmstone
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 --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_MD51
 
@@ -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 inlinee_lines_hasher : free_ptr_hash 
+{
+  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 *types_htab;
+static hash_table *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_htab;
+static hash_table *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)
-   

[PATCH 1/4] Add block parameter to begin_block debug hook

2024-11-06 Thread Mark Harmstone
Add a parameter to the begin_block debug hook that is a pointer to the
tree_node of the block in question. CodeView needs this as it records
line numbers of inlined functions in a different manner, so we need to
be able to tell if the block is actually the start of an inlined
function.

gcc/
* debug.cc (do_nothing_debug_hooks): Change begin_block
function pointer.
(debug_nothing_int_int_tree): New function.
* debug.h (struct gcc_debug_hooks): Add tree parameter to begin_block.
(debug_nothing_int_int_tree): Add declaration.
* dwarf2out.cc (dwarf2out_begin_block): Add tree parameter.
(dwarf2_lineno_debug_hooks): Use new dummy function for begin_block.
* final.cc (final_scan_insn_1): Pass insn block through to
debug_hooks->begin_block.
* vmsdbgout.cc (vmsdbgout_begin_block): Add tree parameter.
---
 gcc/debug.cc | 9 -
 gcc/debug.h  | 3 ++-
 gcc/dwarf2out.cc | 7 ---
 gcc/final.cc | 2 +-
 gcc/vmsdbgout.cc | 5 +++--
 5 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/gcc/debug.cc b/gcc/debug.cc
index 81f6683f4e1..65725decdb1 100644
--- a/gcc/debug.cc
+++ b/gcc/debug.cc
@@ -32,7 +32,7 @@ const struct gcc_debug_hooks do_nothing_debug_hooks =
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
   debug_nothing_int,
-  debug_nothing_int_int,/* begin_block */
+  debug_nothing_int_int_tree,   /* begin_block */
   debug_nothing_int_int,/* end_block */
   debug_true_const_tree,/* ignore_block */
   debug_nothing_int_int_charstar_int_bool, /* source_line */
@@ -146,6 +146,13 @@ debug_nothing_int_int (unsigned int line ATTRIBUTE_UNUSED,
 {
 }
 
+void
+debug_nothing_int_int_tree (unsigned int line ATTRIBUTE_UNUSED,
+   unsigned int n ATTRIBUTE_UNUSED,
+   tree block ATTRIBUTE_UNUSED)
+{
+}
+
 void
 debug_nothing_tree_int (tree decl ATTRIBUTE_UNUSED,
int local ATTRIBUTE_UNUSED)
diff --git a/gcc/debug.h b/gcc/debug.h
index 1d6642aad03..7d35d7d5877 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -57,7 +57,7 @@ struct gcc_debug_hooks
 
   /* Record the beginning of block N, counting from 1 and not
  including the function-scope block, at LINE.  */
-  void (* begin_block) (unsigned int line, unsigned int n);
+  void (* begin_block) (unsigned int line, unsigned int n, tree block);
 
   /* Record the end of a block.  Arguments as for begin_block.  */
   void (* end_block) (unsigned int line, unsigned int n);
@@ -223,6 +223,7 @@ extern void debug_nothing_int_int_charstar_int_bool 
(unsigned int,
 int, bool);
 extern void debug_nothing_int (unsigned int);
 extern void debug_nothing_int_int (unsigned int, unsigned int);
+extern void debug_nothing_int_int_tree (unsigned int, unsigned int, tree);
 extern void debug_nothing_tree (tree);
 extern void debug_nothing_tree_tree (tree, tree);
 extern void debug_nothing_tree_int (tree, int);
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index a2621ec7c76..698926b41c0 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -2866,7 +2866,7 @@ static void dwarf2out_undef (unsigned int, const char *);
 static void dwarf2out_start_source_file (unsigned, const char *);
 static void dwarf2out_end_source_file (unsigned);
 static void dwarf2out_function_decl (tree);
-static void dwarf2out_begin_block (unsigned, unsigned);
+static void dwarf2out_begin_block (unsigned, unsigned, tree);
 static void dwarf2out_end_block (unsigned, unsigned);
 static bool dwarf2out_ignore_block (const_tree);
 static void dwarf2out_set_ignored_loc (unsigned, unsigned, const char *);
@@ -2951,7 +2951,7 @@ const struct gcc_debug_hooks dwarf2_lineno_debug_hooks =
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
   debug_nothing_int,
-  debug_nothing_int_int,/* begin_block */
+  debug_nothing_int_int_tree,   /* begin_block */
   debug_nothing_int_int,/* end_block */
   debug_true_const_tree,/* ignore_block */
   dwarf2out_source_line,/* source_line */
@@ -27806,7 +27806,8 @@ dwarf2out_function_decl (tree decl)
 
 static void
 dwarf2out_begin_block (unsigned int line ATTRIBUTE_UNUSED,
-  unsigned int blocknum)
+  unsigned int blocknum,
+  tree block ATTRIBUTE_UNUSED)
 {
   switch_to_section (current_function_section ());
   ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_BEGIN_LABEL, blocknum);
diff --git a/gcc/final.cc b/gcc/final.cc
index 11141f2b56c..fa5b46e13b2 100644
--- a/gcc/final.cc
+++ b/gcc/final.cc
@@ -2300,7 +2300,7 @@ final_scan_insn_1 (rtx_insn *insn, FILE *file, int 
optimize_p ATTRIBUTE_UNUSED,
 
  /* Output debugging info about the symbol-block beginning.  */
  if (!DECL_IGNORED_P (current_function_decl))
-   debug_hooks->begin_bloc

[PATCH 2/4] Don't output CodeView line numbers for inlined functions

2024-11-06 Thread Mark Harmstone
If we encounter an inlined function, treat it as another
codeview_function, and skip over these when outputting line numbers.
This information will instead be output as part of the S_INLINESITE
symbols.

gcc/
* dwarf2codeview.cc (struct codeview_function): Add parent and
inline_block fields.
(cur_func): New global variable.
(new_codeview_function): New function.
(codeview_source_line): Call new_codeview_function, and use cur_func
instead of last_func.
(codeview_begin_block): New function.
(codeview_end_block): New function.
(write_line_numbers): No longer free data as we go along.
(codeview_switch_text_section): Call new_codeview_function, and use
cur_func instead of last_func.
(codeview_end_epilogue): Use cur_func instead of last_func.
(codeview_debug_finish): Free funcs list and its contents.
* dwarf2codeview.h (codeview_begin_block): Add declaration.
(codeview_end_block): Add declaration.
* dwarf2out.cc (dwarf2out_begin_block): Call codeview_begin_block if
outputting CodeView debug info.
(dwarf2out_end_block): Call codeview_end_block if outputting CodeView
debug info.
---
 gcc/dwarf2codeview.cc | 199 --
 gcc/dwarf2codeview.h  |   2 +
 gcc/dwarf2out.cc  |  10 +++
 3 files changed, 145 insertions(+), 66 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 5e8a4ab39e7..df59fbcf288 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1146,6 +1146,8 @@ struct codeview_function
   function *func;
   unsigned int end_label;
   codeview_line_block *blocks, *last_block;
+  codeview_function *parent;
+  unsigned int inline_block;
 };
 
 struct codeview_symbol
@@ -1417,7 +1419,7 @@ static unsigned int num_files;
 static uint32_t string_offset = 1;
 static hash_table *strings_htab;
 static codeview_string *strings, *last_string;
-static codeview_function *funcs, *last_func;
+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;
@@ -1433,6 +1435,30 @@ 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);
 
+/* Allocate and initialize a codeview_function struct.  */
+
+static codeview_function *
+new_codeview_function (void)
+{
+  codeview_function *f = (codeview_function *)
+   xmalloc (sizeof (codeview_function));
+
+  f->next = NULL;
+  f->func = cfun;
+  f->end_label = 0;
+  f->blocks = f->last_block = NULL;
+  f->inline_block = 0;
+
+  if (!funcs)
+funcs = f;
+  else
+last_func->next = f;
+
+  last_func = f;
+
+  return f;
+}
+
 /* Record new line number against the current function.  */
 
 void
@@ -1444,22 +1470,13 @@ codeview_source_line (unsigned int line_no, const char 
*filename)
 
   targetm.asm_out.internal_label (asm_out_file, LINE_LABEL, label_num);
 
-  if (!last_func || last_func->func != cfun)
+  if (!cur_func || cur_func->func != cfun)
 {
-  codeview_function *f = (codeview_function *)
-   xmalloc (sizeof (codeview_function));
+  codeview_function *f = new_codeview_function ();
 
-  f->next = NULL;
-  f->func = cfun;
-  f->end_label = 0;
-  f->blocks = f->last_block = NULL;
-
-  if (!funcs)
-   funcs = f;
-  else
-   last_func->next = f;
+  f->parent = NULL;
 
-  last_func = f;
+  cur_func = f;
 }
 
   if (filename != last_filename)
@@ -1484,7 +1501,7 @@ codeview_source_line (unsigned int line_no, const char 
*filename)
}
 }
 
-  if (!last_func->last_block || last_func->last_block->file_id != file_id)
+  if (!cur_func->last_block || cur_func->last_block->file_id != file_id)
 {
   codeview_line_block *b;
 
@@ -1495,16 +1512,16 @@ codeview_source_line (unsigned int line_no, const char 
*filename)
   b->num_lines = 0;
   b->lines = b->last_line = NULL;
 
-  if (!last_func->blocks)
-   last_func->blocks = b;
+  if (!cur_func->blocks)
+   cur_func->blocks = b;
   else
-   last_func->last_block->next = b;
+   cur_func->last_block->next = b;
 
-  last_func->last_block = b;
+  cur_func->last_block = b;
 }
 
-  if (last_func->last_block->last_line
-&& last_func->last_block->last_line->line_no == line_no)
+  if (cur_func->last_block->last_line
+&& cur_func->last_block->last_line->line_no == line_no)
 return;
 
   l = (codeview_line *) xmalloc (sizeof (codeview_line));
@@ -1513,13 +1530,45 @@ codeview_source_line (unsigned int line_no, const char 
*filename)
   l->line_no = line_no;
   l->label_num = label_num;
 
-  if (!last_func->last_block->lines)
-last_func->last_block->lines = l;
+  if (!cur_func->last_block->lines)
+cu

[PATCH 4/4] Write S_INLINESITE CodeView symbols

2024-11-06 Thread Mark Harmstone
Translate DW_TAG_inlined_subroutine DIEs into S_INLINESITE CodeView
symbols, marking inlined functions.

gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_INLINESITE and
S_INLINESITE_END.
(get_func_id): Add declaration.
(write_s_inlinesite): New function.
(write_inlinesite_records): New function.
(write_function): Call write_inlinesite_records.
---
 gcc/dwarf2codeview.cc | 124 ++
 1 file changed, 124 insertions(+)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 4fcf3f5f1e2..edaa53f270f 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -86,6 +86,8 @@ enum cv_sym_type {
   S_DEFRANGE_REGISTER_REL = 0x1145,
   S_LPROC32_ID = 0x1146,
   S_GPROC32_ID = 0x1147,
+  S_INLINESITE = 0x114d,
+  S_INLINESITE_END = 0x114e,
   S_PROC_ID_END = 0x114f
 };
 
@@ -1461,6 +1463,8 @@ static uint32_t get_type_num_subroutine_type (dw_die_ref 
type, bool in_struct,
  uint32_t this_type,
  int32_t this_adjustment);
 static void write_cv_padding (size_t padding);
+static uint32_t get_func_id (dw_die_ref die);
+static void write_inlinesite_records (dw_die_ref func, dw_die_ref die);
 
 /* Return the file ID corresponding to a given source filename.  */
 
@@ -3317,6 +3321,124 @@ write_optimized_static_local_vars (dw_die_ref die)
   while (c != first_child);
 }
 
+/* Write an S_INLINESITE symbol, to record that a function has been inlined
+   inside another function.  */
+
+static void
+write_s_inlinesite (dw_die_ref parent_func, dw_die_ref die)
+{
+  unsigned int label_num = ++sym_label_num;
+  dw_die_ref func;
+  uint32_t func_id;
+
+  func = get_AT_ref (die, DW_AT_abstract_origin);
+  if (!func)
+return;
+
+  func_id = get_func_id (func);
+  if (func_id == 0)
+return;
+
+  /* This is struct inline_site in binutils and INLINESITESYM in Microsoft's
+ cvinfo.h:
+
+  struct inline_site
+  {
+   uint16_t size;
+   uint16_t kind;
+   uint32_t parent;
+   uint32_t end;
+   uint32_t inlinee;
+   uint8_t binary_annotations[];
+  } ATTRIBUTE_PACKED;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_INLINESITE);
+  putc ('\n', asm_out_file);
+
+  /* parent, filled in by linker */
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  /* end, filled in by linker */
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, 0);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, func_id);
+  putc ('\n', asm_out_file);
+
+  /* FIXME: there now should follow some "binary annotations", recording how
+   offsets in the function map to line numbers in the inlined function,
+   but these require support in GAS.  */
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+
+  write_inlinesite_records (parent_func, die);
+
+  /* Write S_INLINESITE_END symbol.  */
+
+  label_num = ++sym_label_num;
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+  "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
+  label_num, label_num);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, S_INLINESITE_END);
+  putc ('\n', asm_out_file);
+
+  targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
+}
+
+/* Loop through a function, and translate its DW_TAG_inlined_subroutine DIEs
+   into CodeView S_INLINESITE symbols.  */
+
+static void
+write_inlinesite_records (dw_die_ref func, dw_die_ref die)
+{
+  dw_die_ref first_child, c;
+
+  first_child = dw_get_die_child (die);
+
+  if (!first_child)
+return;
+
+  c = first_child;
+  do
+{
+  c = dw_get_die_sib (c);
+
+  switch (dw_get_die_tag (c))
+   {
+   case DW_TAG_lexical_block:
+ write_inlinesite_records (func, c);
+ break;
+
+   case DW_TAG_inlined_subroutine:
+ write_s_inlinesite (func, c);
+ break;
+
+   default:
+ break;
+   }
+}
+  while (c != first_child);
+}
+
 /* Write an S_GPROC32_ID symbol, representing a global function, or an
S_LPROC32_ID symbol, for a static function.  */
 
@@ -3443,6 +3565,8 @@ write_function (codeview_symbol *s)
 
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 
+  write_inlinesite_records (s->function.die, s->function.

[PATCH] Write binary annotations for CodeView S_INLINESITE symbols

2024-11-17 Thread Mark Harmstone
Add "binary annotations" at the end of CodeView S_INLINESITE symbols,
which are a series of compressed integers that represent how line
numbers map to addresses.

This requires assembler support; you will need commit b3aa594d ("gas:
add .cv_ucomp and .cv_scomp pseudo-directives") in binutils.

gcc/
* dwarf2codeview.cc (enum binary_annotation_opcode): Define.
(struct codeview_function): Add htab_next and inline_loc;
(struct cv_func_hasher): Define.
(cv_func_htab): New global variable.
(new_codeview_function): Add new codeview_function to hash table.
(codeview_begin_block): Record location of inline block.
(codeview_end_block): Add dummy source line at end of inline block.
(find_line_function): New function.
(write_binary_annotations): New function.
(write_s_inlinesite): Call write_binary_annotations.
(codeview_debug_finish): Delete cv_func_htab.
---
(This goes against my patch "Write S_INLINESITE CodeView symbols". I
held off from submitting because I was waiting for the binutils patch to
go in, but I want to get this out there before the code freeze.)

 gcc/dwarf2codeview.cc | 195 +-
 1 file changed, 191 insertions(+), 4 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 4dd1bff6bc6..08fbe7f5bb6 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1085,6 +1085,25 @@ enum cv_amd64_register {
   CV_AMD64_YMM15D3 = 687
 };
 
+/* This is enum BinaryAnnotationOpcode in Microsoft's cvinfo.h.  */
+
+enum binary_annotation_opcode {
+  ba_op_invalid,
+  ba_op_code_offset,
+  ba_op_change_code_offset_base,
+  ba_op_change_code_offset,
+  ba_op_change_code_length,
+  ba_op_change_file,
+  ba_op_change_line_offset,
+  ba_op_change_line_end_delta,
+  ba_op_change_range_kind,
+  ba_op_change_column_start,
+  ba_op_change_column_end_delta,
+  ba_op_change_code_offset_and_line_offset,
+  ba_op_change_code_length_and_code_offset,
+  ba_op_change_column_end
+};
+
 struct codeview_string
 {
   codeview_string *next;
@@ -1156,11 +1175,13 @@ struct codeview_inlinee_lines
 struct codeview_function
 {
   codeview_function *next;
+  codeview_function *htab_next;
   function *func;
   unsigned int end_label;
   codeview_line_block *blocks, *last_block;
   codeview_function *parent;
   unsigned int inline_block;
+  location_t inline_loc;
 };
 
 struct codeview_symbol
@@ -1439,6 +1460,16 @@ struct inlinee_lines_hasher : free_ptr_hash 
   }
 };
 
+struct cv_func_hasher : nofree_ptr_hash 
+{
+  typedef dw_die_ref compare_type;
+
+  static bool equal (const codeview_function *f, dw_die_ref die)
+  {
+return lookup_decl_die (f->func->decl) == die;
+  }
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -1456,6 +1487,7 @@ static codeview_custom_type *custom_types, 
*last_custom_type;
 static codeview_deferred_type *deferred_types, *last_deferred_type;
 static hash_table *string_id_htab;
 static hash_table *inlinee_lines_htab;
+static hash_table *cv_func_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,
@@ -1504,14 +1536,18 @@ get_file_id (const char *filename)
 static codeview_function *
 new_codeview_function (void)
 {
+  codeview_function **slot;
+  dw_die_ref die;
   codeview_function *f = (codeview_function *)
xmalloc (sizeof (codeview_function));
 
   f->next = NULL;
+  f->htab_next = NULL;
   f->func = cfun;
   f->end_label = 0;
   f->blocks = f->last_block = NULL;
   f->inline_block = 0;
+  f->inline_loc = 0;
 
   if (!funcs)
 funcs = f;
@@ -1520,6 +1556,18 @@ new_codeview_function (void)
 
   last_func = f;
 
+  if (!cv_func_htab)
+cv_func_htab = new hash_table (10);
+
+  die = lookup_decl_die (cfun->decl);
+
+  slot = cv_func_htab->find_slot_with_hash (die, htab_hash_pointer (die),
+   INSERT);
+  if (*slot)
+f->htab_next = *slot;
+
+  *slot = f;
+
   return f;
 }
 
@@ -1598,6 +1646,7 @@ codeview_begin_block (unsigned int line ATTRIBUTE_UNUSED,
 
   f->parent = cur_func;
   f->inline_block = blocknum;
+  f->inline_loc = locus;
 
   cur_func = f;
 }
@@ -1610,7 +1659,13 @@ void
 codeview_end_block (unsigned int line ATTRIBUTE_UNUSED, unsigned int blocknum)
 {
   if (cur_func && cur_func->inline_block == blocknum)
-cur_func = cur_func->parent;
+{
+  /* If inlined function, add dummy source line at the end so we know how
+long the actual last line is.  */
+  codeview_source_line (0, "");
+
+  cur_func = cur_func->parent;
+}
 }
 
 /* Adds string to the string table, returning its offset.  If already present,
@@ -3321,6 +3376,133 @@ write_optimized_static_local_vars (dw_die_ref die)
   while (c != first_child);
 }
 
+/* Given a DW_TAG_inlined_subrou

Re: [PATCH 2/2] Flatten anonymous structs in CodeView types

2024-11-18 Thread Mark Harmstone

On 18/11/2024 05:05, Jeff Law wrote:



On 11/17/24 9:01 AM, Mark Harmstone wrote:

If a CodeView struct, class, or union has as a member an anonymous
struct, class, or union, this gets flattened. The sub-struct's members
will appear as if they were part of their parent.

For this, we move part of get_type_num_struct into a new function
add_to_fieldlist, which also handles creating an LF_INDEX overflow item
if an LF_FIELDLIST grows too large. This is because add_struct_member
now calls itself recursively, and so needs to handle overflows itself.

gcc/
* dwarf2codeview.cc (add_to_fieldlist): New function.
(add_struct_member): Call recursively to flatten structs, and call
add_to_fieldlist.
(add_struct_static_member): Call add_to_fieldlist.
(add_struct_function): Call add_to_fieldlist.
(add_struct_inheritance): Call add_to_fieldlist.
(add_struct_nested_type): Call add_to_fieldlist.
(get_type_num_struct): Move code to add_to_fieldlist, and move
responsibility for this to subfunctions.



@@ -5933,36 +5979,69 @@ create_bitfield (dw_die_ref c)
  static void
  add_struct_member (dw_die_ref c, uint16_t accessibility,
-   codeview_subtype **el, size_t *el_len)
+   codeview_custom_type **ct, uint16_t *num_members,
+   unsigned int base_offset)
  {
-  *el = (codeview_subtype *) xmalloc (sizeof (**el));
-  (*el)->next = NULL;
-  (*el)->kind = LF_MEMBER;
-  (*el)->lf_member.attributes = accessibility;
+  codeview_subtype *el;
+  size_t el_len;
+  dw_die_ref type = get_AT_ref (c, DW_AT_type);
+  unsigned int offset;
+
+  offset = base_offset + get_AT_unsigned (c, DW_AT_data_member_location);
+
+  /* If the data member is actually an anonymous struct, class, or union,
+ follow MSVC by flattening this into its parent.  */
+  if (!get_AT_string (c, DW_AT_name) && type
+  && (dw_get_die_tag (type) == DW_TAG_structure_type
+  || dw_get_die_tag (type) == DW_TAG_class_type
+  || dw_get_die_tag (type) == DW_TAG_union_type))

I suspect there's a formatting goof here.  The || lines should be linking up under the 
first "dw_get_die_tag" call.  It's possible this is just a mailer goof, but 
it's definitely worth checking.

OK with the formatting fix noted above.  Or if no fix is needed, then OK as-is.

Thanks,
Jeff


Thanks Jeff. My mistake, I'll fix it when I push it.

Mark


Re: [PATCH] Write binary annotations for CodeView S_INLINESITE symbols

2024-11-18 Thread Mark Harmstone

On 18/11/2024 04:57, Jeff Law wrote:



On 11/17/24 8:40 AM, Mark Harmstone wrote:

Add "binary annotations" at the end of CodeView S_INLINESITE symbols,
which are a series of compressed integers that represent how line
numbers map to addresses.

This requires assembler support; you will need commit b3aa594d ("gas:
add .cv_ucomp and .cv_scomp pseudo-directives") in binutils.

gcc/
* dwarf2codeview.cc (enum binary_annotation_opcode): Define.
(struct codeview_function): Add htab_next and inline_loc;
(struct cv_func_hasher): Define.
(cv_func_htab): New global variable.
(new_codeview_function): Add new codeview_function to hash table.
(codeview_begin_block): Record location of inline block.
(codeview_end_block): Add dummy source line at end of inline block.
(find_line_function): New function.
(write_binary_annotations): New function.
(write_s_inlinesite): Call write_binary_annotations.
(codeview_debug_finish): Delete cv_func_htab.
---
(This goes against my patch "Write S_INLINESITE CodeView symbols". I
held off from submitting because I was waiting for the binutils patch to
go in, but I want to get this out there before the code freeze.)

So one of the things we often do in these scenarios is write a configure test 
which checks if the assembler supports the feature we care about. if not, then 
we disable the compiler feature that's dependent upon the assembler bits.  Look 
for HAVE_GAS_* and HAVE_AS_*.

I don't have a good sense of how widely the CodeView stuff is being used, so no 
good sense what the right thing to do WRT that issue would be.


So no concerns on the implementation details, we just need to reach a 
conclusion on what to do with the assembler dependency.

jeff


Thanks Jeff, I'll code up a version that does checking in ./configure.

I know you're probably inundated with patches at the moment, but there was a 
set of 4 S_INLINESITE patches
I submitted on the 6th; can I just make sure they've not been missed?

Thanks

Mark


[PATCH 2/2] Flatten anonymous structs in CodeView types

2024-11-17 Thread Mark Harmstone
If a CodeView struct, class, or union has as a member an anonymous
struct, class, or union, this gets flattened. The sub-struct's members
will appear as if they were part of their parent.

For this, we move part of get_type_num_struct into a new function
add_to_fieldlist, which also handles creating an LF_INDEX overflow item
if an LF_FIELDLIST grows too large. This is because add_struct_member
now calls itself recursively, and so needs to handle overflows itself.

gcc/
* dwarf2codeview.cc (add_to_fieldlist): New function.
(add_struct_member): Call recursively to flatten structs, and call
add_to_fieldlist.
(add_struct_static_member): Call add_to_fieldlist.
(add_struct_function): Call add_to_fieldlist.
(add_struct_inheritance): Call add_to_fieldlist.
(add_struct_nested_type): Call add_to_fieldlist.
(get_type_num_struct): Move code to add_to_fieldlist, and move
responsibility for this to subfunctions.
---
 gcc/dwarf2codeview.cc | 280 +-
 1 file changed, 167 insertions(+), 113 deletions(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 261fcea6a97..59380335991 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -5901,6 +5901,52 @@ add_struct_forward_def (dw_die_ref type)
   return ct->num;
 }
 
+/* Add a new subtype to an LF_FIELDLIST type, and handle overflows if
+   necessary.  */
+
+static void
+add_to_fieldlist (codeview_custom_type **ct, uint16_t *num_members,
+ codeview_subtype *el, size_t el_len)
+{
+  /* Add an LF_INDEX subtype if everything's too big for one
+ LF_FIELDLIST.  */
+
+  if ((*ct)->lf_fieldlist.length + el_len > MAX_FIELDLIST_SIZE)
+{
+  codeview_subtype *idx;
+  codeview_custom_type *ct2;
+
+  idx = (codeview_subtype *) xmalloc (sizeof (*idx));
+  idx->next = NULL;
+  idx->kind = LF_INDEX;
+  idx->lf_index.type_num = 0;
+
+  (*ct)->lf_fieldlist.last_subtype->next = idx;
+  (*ct)->lf_fieldlist.last_subtype = idx;
+
+  ct2 = (codeview_custom_type *)
+   xmalloc (sizeof (codeview_custom_type));
+
+  ct2->next = *ct;
+  ct2->kind = LF_FIELDLIST;
+  ct2->lf_fieldlist.length = 0;
+  ct2->lf_fieldlist.subtypes = NULL;
+  ct2->lf_fieldlist.last_subtype = NULL;
+
+  *ct = ct2;
+}
+
+  (*ct)->lf_fieldlist.length += el_len;
+
+  if ((*ct)->lf_fieldlist.last_subtype)
+(*ct)->lf_fieldlist.last_subtype->next = el;
+  else
+(*ct)->lf_fieldlist.subtypes = el;
+
+  (*ct)->lf_fieldlist.last_subtype = el;
+  (*num_members)++;
+}
+
 /* Add an LF_BITFIELD type, returning its number.  DWARF represents bitfields
as members in a struct with a DW_AT_data_bit_offset attribute, whereas in
CodeView they're a distinct type.  */
@@ -5933,36 +5979,69 @@ create_bitfield (dw_die_ref c)
 
 static void
 add_struct_member (dw_die_ref c, uint16_t accessibility,
-  codeview_subtype **el, size_t *el_len)
+  codeview_custom_type **ct, uint16_t *num_members,
+  unsigned int base_offset)
 {
-  *el = (codeview_subtype *) xmalloc (sizeof (**el));
-  (*el)->next = NULL;
-  (*el)->kind = LF_MEMBER;
-  (*el)->lf_member.attributes = accessibility;
+  codeview_subtype *el;
+  size_t el_len;
+  dw_die_ref type = get_AT_ref (c, DW_AT_type);
+  unsigned int offset;
+
+  offset = base_offset + get_AT_unsigned (c, DW_AT_data_member_location);
+
+  /* If the data member is actually an anonymous struct, class, or union,
+ follow MSVC by flattening this into its parent.  */
+  if (!get_AT_string (c, DW_AT_name) && type
+  && (dw_get_die_tag (type) == DW_TAG_structure_type
+  || dw_get_die_tag (type) == DW_TAG_class_type
+  || dw_get_die_tag (type) == DW_TAG_union_type))
+{
+  dw_die_ref c2, first_child;
+
+  first_child = dw_get_die_child (type);
+  c2 = first_child;
+
+  do
+   {
+ c2 = dw_get_die_sib (c2);
+
+ if (dw_get_die_tag (c2) == DW_TAG_member)
+ add_struct_member (c2, accessibility, ct, num_members, offset);
+   }
+  while (c2 != first_child);
+
+  return;
+}
+
+  el = (codeview_subtype *) xmalloc (sizeof (*el));
+  el->next = NULL;
+  el->kind = LF_MEMBER;
+  el->lf_member.attributes = accessibility;
 
   if (get_AT (c, DW_AT_data_bit_offset))
-(*el)->lf_member.type = create_bitfield (c);
+el->lf_member.type = create_bitfield (c);
   else
-(*el)->lf_member.type = get_type_num (get_AT_ref (c, DW_AT_type),
- true, false);
+el->lf_member.type = get_type_num (type, true, false);
 
-  (*el)->lf_member.offset.neg = false;
-  (*el)->lf_member.offset.num = get_AT_unsigned (c, 
DW_AT_data_member_location);
+  el->lf_member.offset.neg = false;
+  el->lf_member.offset.num = offset;
 
-  *el_len = 11 + cv_integer_len (&(*el)->lf_member.offset);
+  el_len = 11 + cv_integer_len (&el->lf_member.offset);
 
   if (get

[PATCH 1/2] Produce CodeView info about nested types

2024-11-17 Thread Mark Harmstone
If the DIE for a struct, class, or union contains a nested type, add a
LF_NESTTYPE entry to its field list recording this.

Plus if we use a nested type, make sure that its parent also gets
defined. This may entail adding a forward definition and creating a
deferred type, so we need to call flush_deferred_types in
codeview_debug_finish as well.

gcc/
* dwarf2codeview.cc (enum cv_leaf_type): Add LF_NESTTYPE.
(struct codeview_subtype): Add lf_nesttype to union.
(flush_deferred_types): Add declaration.
(write_lf_fieldlist): Handle LF_NESTTYPE.
(codeview_debug_finish): Call flush_deferred_types.
(add_struct_nested_type): New function.
(get_type_num_struct): Call add_struct_nested_type, and if nested make
that parent is added.
---
(This doesn't logically depend on my pending S_INLINESITE patches, but
does if you are attempting to apply this cleanly. I'm getting this out
before the code freeze.)

 gcc/dwarf2codeview.cc | 89 ++-
 1 file changed, 88 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 08fbe7f5bb6..261fcea6a97 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -116,6 +116,7 @@ enum cv_leaf_type {
   LF_MEMBER = 0x150d,
   LF_STMEMBER = 0x150e,
   LF_METHOD = 0x150f,
+  LF_NESTTYPE = 0x1510,
   LF_ONEMETHOD = 0x1511,
   LF_FUNC_ID = 0x1601,
   LF_MFUNC_ID = 0x1602,
@@ -1285,6 +1286,11 @@ struct codeview_subtype
   uint32_t base_class_type;
   codeview_integer offset;
 } lf_bclass;
+struct
+{
+  uint32_t type;
+  char *name;
+} lf_nesttype;
   };
 };
 
@@ -1497,6 +1503,7 @@ static uint32_t get_type_num_subroutine_type (dw_die_ref 
type, bool in_struct,
 static void write_cv_padding (size_t padding);
 static uint32_t get_func_id (dw_die_ref die);
 static void write_inlinesite_records (dw_die_ref func, dw_die_ref die);
+static void flush_deferred_types (void);
 
 /* Return the file ID corresponding to a given source filename.  */
 
@@ -4364,6 +4371,40 @@ write_lf_fieldlist (codeview_custom_type *t)
  write_cv_padding (4 - (leaf_len % 4));
  break;
 
+   case LF_NESTTYPE:
+ /* This is lf_nest_type in binutils and lfNestType in Microsoft's
+cvinfo.h:
+
+   struct lf_nest_type
+   {
+ uint16_t kind;
+ uint16_t padding;
+ uint32_t type;
+ char name[];
+   } ATTRIBUTE_PACKED;
+ */
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, LF_NESTTYPE);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (2, false), asm_out_file);
+ fprint_whex (asm_out_file, 0);
+ putc ('\n', asm_out_file);
+
+ fputs (integer_asm_op (4, false), asm_out_file);
+ fprint_whex (asm_out_file, v->lf_nesttype.type);
+ putc ('\n', asm_out_file);
+
+ name_len = strlen (v->lf_nesttype.name) + 1;
+ ASM_OUTPUT_ASCII (asm_out_file, v->lf_nesttype.name, name_len);
+
+ leaf_len = 8 + name_len;
+ write_cv_padding (4 - (leaf_len % 4));
+
+ free (v->lf_nesttype.name);
+ break;
+
default:
  break;
}
@@ -5137,6 +5178,12 @@ codeview_debug_finish (void)
 
   write_codeview_symbols ();
 
+  /* If we reference a nested struct but not its parent, add_deferred_type
+ gets called if we create a forward reference for this, even though we've
+ already flushed this in codeview_debug_early_finish.  In this case we will
+ need to flush this list again.  */
+  flush_deferred_types ();
+
   if (custom_types)
 write_custom_types ();
 
@@ -6135,6 +6182,32 @@ is_templated_func (dw_die_ref die)
   return false;
 }
 
+/* Create a field list subtype that records that a struct has a nested type
+   contained within it.  */
+
+static void
+add_struct_nested_type (dw_die_ref c, codeview_subtype **el, size_t *el_len)
+{
+  const char *name = get_AT_string (c, DW_AT_name);
+  size_t name_len;
+
+  if (!name)
+return;
+
+  name_len = strlen (name);
+
+  *el = (codeview_subtype *) xmalloc (sizeof (**el));
+  (*el)->next = NULL;
+  (*el)->kind = LF_NESTTYPE;
+  (*el)->lf_nesttype.type = get_type_num (c, true, false);
+  (*el)->lf_nesttype.name = xstrdup (name);
+
+  *el_len = 9 + name_len;
+
+  if (*el_len % 4)
+*el_len += 4 - (*el_len % 4);
+}
+
 /* Process a DW_TAG_structure_type, DW_TAG_class_type, or DW_TAG_union_type
DIE, add an LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS / LF_UNION type,
and return the number of the latter.  */
@@ -6142,11 +6215,18 @@ is_templated_func (dw_die_ref die)
 static uint32_t
 get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref)
 {
-  dw_die_ref first_child;
+  dw_die_ref parent, first_child;
   codeview_custom_type *ct;
   uint16_t num_members = 0;
   uint32_t last_type = 0;
 
+  parent = dw_get_die_parent(type);

[PATCH v2] Write binary annotations for CodeView S_INLINESITE symbols

2024-11-30 Thread Mark Harmstone
Add "binary annotations" at the end of CodeView S_INLINESITE symbols,
which are a series of compressed integers that represent how line
numbers map to addresses.

This requires assembler support; you will need commit b3aa594d ("gas:
add .cv_ucomp and .cv_scomp pseudo-directives") in binutils.

gcc/
* configure.ac (HAVE_GAS_CV_UCOMP): New check.
* configure: Regenerate.
* config.in: Regenerate.
* dwarf2codeview.cc (enum binary_annotation_opcode): Define.
(struct codeview_function): Add htab_next and inline_loc;
(struct cv_func_hasher): Define.
(cv_func_htab): New global variable.
(new_codeview_function): Add new codeview_function to hash table.
(codeview_begin_block): Record location of inline block.
(codeview_end_block): Add dummy source line at end of inline block.
(find_line_function): New function.
(write_binary_annotations): New function.
(write_s_inlinesite): Call write_binary_annotations.
(codeview_debug_finish): Delete cv_func_htab.
---
This second version adds a configure check for .cv_ucomp in the
assembler, and prevents the binary annotations from being emitted if
it's not supported. There's no separate check for .cv_scomp, as the same
binutils commit introduced them both.

 gcc/config.in |   6 ++
 gcc/configure |  32 +++
 gcc/configure.ac  |   3 +
 gcc/dwarf2codeview.cc | 203 +-
 4 files changed, 240 insertions(+), 4 deletions(-)

diff --git a/gcc/config.in b/gcc/config.in
index 972c0c2034d..d8145a1453b 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1459,6 +1459,12 @@
 /* Define 0/1 if your assembler supports .cfi_sections. */
 #undef HAVE_GAS_CFI_SECTIONS_DIRECTIVE
 
+/* Define if your assembler supports .cv_ucomp. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GAS_CV_UCOMP
+#endif
+
+
 /* Define if your assembler supports the .loc discriminator sub-directive. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_GAS_DISCRIMINATOR
diff --git a/gcc/configure b/gcc/configure
index 8bb71cfe348..bab4181a940 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -25943,6 +25943,38 @@ $as_echo "#define HAVE_GAS_BASE64 1" >>confdefs.h
 fi
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .cv_ucomp" >&5
+$as_echo_n "checking assembler for .cv_ucomp... " >&6; }
+if ${gcc_cv_as_cv_ucomp+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_cv_ucomp=no
+  if test x$gcc_cv_as != x; then
+$as_echo '.cv_ucomp 0' > conftest.s
+if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+then
+   gcc_cv_as_cv_ucomp=yes
+else
+  echo "configure: failed program was" >&5
+  cat conftest.s >&5
+fi
+rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_cv_ucomp" >&5
+$as_echo "$gcc_cv_as_cv_ucomp" >&6; }
+if test $gcc_cv_as_cv_ucomp = yes; then
+
+$as_echo "#define HAVE_GAS_CV_UCOMP 1" >>confdefs.h
+
+fi
+
+
 # gnu_indirect_function type is an extension proposed at
 # http://groups.google/com/group/generic-abi/files. It allows dynamic runtime
 # selection of function implementation
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 88a1a44fcf7..b1b21cf4d7b 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -3065,6 +3065,9 @@ gcc_GAS_CHECK_FEATURE([.base64], gcc_cv_as_base64,,
.base64 
"Tm9uIHB1ZG9yIGVzdCBuaWwgc2NpcmUsIHB1ZG9yIG5pbCBkaXNjZXJlIHZlbGxlLgo="],,
 [AC_DEFINE(HAVE_GAS_BASE64, 1, [Define if your assembler supports .base64.])])
 
+gcc_GAS_CHECK_FEATURE([.cv_ucomp], gcc_cv_as_cv_ucomp,,[.cv_ucomp 0],,
+[AC_DEFINE(HAVE_GAS_CV_UCOMP, 1, [Define if your assembler supports 
.cv_ucomp.])])
+
 # gnu_indirect_function type is an extension proposed at
 # http://groups.google/com/group/generic-abi/files. It allows dynamic runtime
 # selection of function implementation
diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 821554088d7..33b57a45970 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -1086,6 +1086,25 @@ enum cv_amd64_register {
   CV_AMD64_YMM15D3 = 687
 };
 
+/* This is enum BinaryAnnotationOpcode in Microsoft's cvinfo.h.  */
+
+enum binary_annotation_opcode {
+  ba_op_invalid,
+  ba_op_code_offset,
+  ba_op_change_code_offset_base,
+  ba_op_change_code_offset,
+  ba_op_change_code_length,
+  ba_op_change_file,
+  ba_op_change_line_offset,
+  ba_op_change_line_end_delta,
+  ba_op_change_range_kind,
+  ba_op_change_column_start,
+  ba_op_change_column_end_delta,
+  ba_op_change_code_offset_and_line_offset,
+  ba_op_change_code_length_and_code_offset,
+  ba_op_change_column_end
+};
+
 struct codeview_string
 {
   codeview_string *next;
@@ -1157,11 +1176,13 @@ struct codeview_inlinee_lines

[PATCH] Fix non-aligned CodeView symbols

2024-12-01 Thread Mark Harmstone
CodeView symbols in PDB files are aligned to four-byte boundaries. It's
not really clear what logic MSVC uses to enforce this; sometimes the
symbols are padded in the object file, sometimes the linker seems to do
the work.

It makes more sense to do this in the compiler, so fix the two instances
where we can write symbols with a non-aligned length. S_FRAMEPROC is
unusually not a multiple of 4, so will always have 2 bytes padding.
S_INLINESITE is followed by variable-length "binary annotations", so
will also usually have padding.

gcc/
* dwarf2codeview.cc (write_s_frameproc): Align output.
(write_s_inlinesite): Align output.
---
 gcc/dwarf2codeview.cc | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 19ec58d096e..a50fcdf9f7b 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -3208,6 +3208,8 @@ write_s_frameproc (void)
   fprint_whex (asm_out_file, 0);
   putc ('\n', asm_out_file);
 
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+
   targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
 }
 
@@ -3576,7 +3578,10 @@ write_s_inlinesite (dw_die_ref parent_func, dw_die_ref 
die)
   line_func = find_line_function (parent_func, die);
 
   if (line_func)
-write_binary_annotations (line_func, func_id);
+{
+  write_binary_annotations (line_func, func_id);
+  ASM_OUTPUT_ALIGN (asm_out_file, 2);
+}
 #else
   (void) line_func;
 #endif
-- 
2.45.2



[PATCH] Fix compilation error in vmsdbgout_begin_block on VMS targets

2024-12-19 Thread Mark Harmstone
Commit 4ed189854eae ("Add block parameter to begin_block debug hook") changed
the definition of the begin_block function pointer to add another parameter,
but I missed a call in vmsdbgout_begin_block.

Fixes bug #118123.

gcc/
* vmsdbgout.cc (vmsdbgout_begin_block): Fix compilation error.
---
 gcc/vmsdbgout.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gcc/vmsdbgout.cc b/gcc/vmsdbgout.cc
index d9e6a8b7b74..204e5695d39 100644
--- a/gcc/vmsdbgout.cc
+++ b/gcc/vmsdbgout.cc
@@ -1231,10 +1231,10 @@ vmsdbgout_end_epilogue (unsigned int line, const char 
*file)
 
 static void
 vmsdbgout_begin_block (unsigned line, unsigned blocknum,
-  tree block ATTRIBUTE_UNUSED)
+  tree block)
 {
   if (write_symbols == VMS_AND_DWARF2_DEBUG)
-(*dwarf2_debug_hooks.begin_block) (line, blocknum);
+(*dwarf2_debug_hooks.begin_block) (line, blocknum, block);
 
   if (debug_info_level > DINFO_LEVEL_TERSE)
 targetm.asm_out.internal_label (asm_out_file, BLOCK_BEGIN_LABEL, blocknum);
-- 
2.45.2