diff mbox series

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

Message ID 20210320162652.23346-13-mark@harmstone.com
State New
Headers show
Series [01/24] Add -gcodeview debugging option | expand

Commit Message

Mark Harmstone March 20, 2021, 4:26 p.m. UTC
---
 gcc/pdbout.c | 449 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/pdbout.h |  43 +++++
 2 files changed, 492 insertions(+)
diff mbox series

Patch

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[] = "<unnamed-tag>";
+
 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 <= 0xffff)
+		{
+		  len += 2;	// LF_USHORT
+		}
+	      else if (fl->entries[i].value >= -0x7fffffff &&
+		       fl->entries[i].value <= 0x7fffffff)
+		{
+		  len += 4;	// LF_LONG
+		}
+	      else if (fl->entries[i].value >= 0x80000000 &&
+		       fl->entries[i].value <= 0xffffffff)
+		{
+		  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 & 0xffff));
+
+		align = (align + 2) % 4;
+	      }
+	    else if (fl->entries[i].value >= 0x8000
+		    && fl->entries[i].value <= 0xffff)
+	      {
+		fprintf (asm_out_file, "\t.short\t0x%x\n", LF_USHORT);
+		fprintf (asm_out_file, "\t.short\t0x%x\n",
+			(unsigned int) ((uint16_t) fl->entries[i].
+					value & 0xffff));
+
+		align = (align + 2) % 4;
+	      }
+	    else if (fl->entries[i].value >= -0x7fffffff
+		    && fl->entries[i].value <= 0x7fffffff)
+	      {
+		fprintf (asm_out_file, "\t.short\t0x%x\n", LF_LONG);
+		fprintf (asm_out_file, "\t.long\t0x%x\n",
+			(int32_t) fl->entries[i].value);
+	      }
+	    else if (fl->entries[i].value >= 0x80000000
+		    && fl->entries[i].value <= 0xffffffff)
+	      {
+		fprintf (asm_out_file, "\t.short\t0x%x\n", LF_ULONG);
+		fprintf (asm_out_file, "\t.long\t0x%x\n",
+			(uint32_t) fl->entries[i].value);
+	      }
+	    else if (fl->entries[i].value < 0)
+	      {
+		fprintf (asm_out_file, "\t.short\t0x%x\n", LF_QUADWORD);
+		fprintf (asm_out_file, "\t.quad\t0x%" PRIx64 "\n",
+			(int64_t) fl->entries[i].value);
+	      }
+	    else
+	      {
+		fprintf (asm_out_file, "\t.short\t0x%x\n", LF_UQUADWORD);
+		fprintf (asm_out_file, "\t.quad\t0x%" PRIx64 "\n",
+			(uint64_t) fl->entries[i].value);
+	      }
+
+	    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 - align;
+
+	    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;
+	  }
+	}
+    }
+}
+
+/* Output a lfEnum structure. */
+static void
+write_enum (struct pdb_enum *en)
+{
+  size_t name_len = en->name ? strlen (en->name) : (sizeof (unnamed) - 1);
+  unsigned int len = 17 + 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_ENUM);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", en->count);
+  fprintf (asm_out_file, "\t.short\t0\n");	// property
+  fprintf (asm_out_file, "\t.short\t0x%x\n", en->type ? en->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");	// padding
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+	   en->field_type ? en->field_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");	// padding
+
+  if (en->name)
+    ASM_OUTPUT_ASCII (asm_out_file, en->name, name_len + 1);
+  else
+    ASM_OUTPUT_ASCII (asm_out_file, unnamed, sizeof (unnamed));
+
+  align = 4 - ((1 + 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 lfPointer structure. */
 static void
 write_pointer (struct pdb_pointer *ptr)
@@ -952,6 +1182,14 @@  write_type (struct pdb_type *t)
 {
   switch (t->cv_type)
     {
+    case LF_FIELDLIST:
+      write_fieldlist ((struct pdb_fieldlist *) t->data);
+      break;
+
+    case LF_ENUM:
+      write_enum ((struct pdb_enum *) t->data);
+      break;
+
     case LF_POINTER:
       if (t->id < FIRST_TYPE_NUM)	// pointer to builtin
 	return;
@@ -1158,6 +1396,87 @@  pdbout_late_global_decl (tree var)
   global_vars = v;
 }
 
+/* Add a fieldlist, which is the basis of enums. */
+static struct pdb_type *
+add_type_fieldlist (struct pdb_type *t)
+{
+  struct pdb_type *type, *last_entry = NULL;
+
+  type = fieldlist_types;
+  while (type)
+    {
+      struct pdb_fieldlist *fl1 = (struct pdb_fieldlist *) t->data;
+      struct pdb_fieldlist *fl2 = (struct pdb_fieldlist *) type->data;
+
+      if (fl1->count == fl2->count)
+	{
+	  bool same = true;
+
+	  for (unsigned int i = 0; i < fl1->count; i++)
+	    {
+	      struct pdb_fieldlist_entry *pfe1 =
+		(struct pdb_fieldlist_entry *) &fl1->entries[i];
+	      struct pdb_fieldlist_entry *pfe2 =
+		(struct pdb_fieldlist_entry *) &fl2->entries[i];
+
+	      if (pfe1->cv_type != pfe2->cv_type)
+		{
+		  same = false;
+		  break;
+		}
+
+	      if (pfe1->cv_type == LF_ENUMERATE)
+		{
+		  if (pfe1->value != pfe2->value ||
+		      ((pfe1->name || pfe2->name) &&
+		       (!pfe1->name || !pfe2->name ||
+			strcmp (pfe1->name, pfe2->name))))
+		    {
+		      same = false;
+		      break;
+		    }
+		}
+	    }
+
+	  if (same)
+	    {
+	      for (unsigned int i = 0; i < fl1->count; i++)
+		{
+		  struct pdb_fieldlist_entry *pfe1 =
+		    (struct pdb_fieldlist_entry *) &fl1->entries[i];
+
+		  if (pfe1->name)
+		    free (pfe1->name);
+		}
+
+	      free (t);
+
+	      return type;
+	    }
+	}
+
+      last_entry = type;
+      type = type->next2;
+    }
+
+  t->next = t->next2 = NULL;
+  t->id = 0;
+
+  if (last_entry)
+    last_entry->next2 = t;
+  else
+    fieldlist_types = t;
+
+  if (last_type)
+    last_type->next = t;
+  else
+    types = t;
+
+  last_type = t;
+
+  return t;
+}
+
 /* Given an array type t, allocate a new pdb_type and add it to the
  * type list. */
 static struct pdb_type *
@@ -1278,6 +1597,133 @@  add_arglist_type (struct pdb_type *t)
   return t;
 }
 
+/* For a given enum, allocate a new pdb_type and add it to the type list. */
+static struct pdb_type *
+find_type_enum (tree t)
+{
+  tree v;
+  struct pdb_type *fltype, *enumtype, *last_entry = NULL;
+  struct pdb_fieldlist *fieldlist;
+  struct pdb_fieldlist_entry *ent;
+  struct pdb_enum *en;
+  unsigned int num_entries;
+  struct pdb_type *en_type;
+  char *name;
+  struct pdb_type **slot;
+
+  v = TYPE_VALUES (t);
+  num_entries = 0;
+
+  while (v)
+    {
+      num_entries++;
+
+      v = TREE_CHAIN (v);
+    }
+
+  // add fieldlist type
+
+  fltype =
+    (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+				 offsetof (struct pdb_fieldlist, entries) +
+				 (num_entries *
+				  sizeof (struct pdb_fieldlist_entry)));
+  fltype->cv_type = LF_FIELDLIST;
+  fltype->tree = NULL;
+
+  fieldlist = (struct pdb_fieldlist *) fltype->data;
+  fieldlist->count = num_entries;
+
+  ent = fieldlist->entries;
+  v = TYPE_VALUES (t);
+
+  while (v)
+    {
+      ent->cv_type = LF_ENUMERATE;
+      ent->fld_attr = 0;
+      ent->type = NULL;
+
+      if (TREE_CODE (TREE_VALUE (v)) == CONST_DECL)
+	ent->value = TREE_INT_CST_ELT (DECL_INITIAL (TREE_VALUE (v)), 0);
+      else if (TREE_CODE (TREE_VALUE (v)) == INTEGER_CST)
+	ent->value = TREE_INT_CST_ELT (TREE_VALUE (v), 0);
+      else
+	ent->value = 0;
+
+      ent->name = xstrdup (IDENTIFIER_POINTER (TREE_PURPOSE (v)));
+
+      v = TREE_CHAIN (v);
+      ent++;
+    }
+
+  fltype = add_type_fieldlist (fltype);
+
+  // add type for enum
+
+  en_type = TREE_TYPE (t) ? find_type (TREE_TYPE (t)) : NULL;
+
+  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] != '.')
+    name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t))));
+  else
+    name = NULL;
+
+  enumtype = enum_types;
+  while (enumtype)
+    {
+      en = (struct pdb_enum *) enumtype->data;
+
+      if (en->count == num_entries &&
+	  en->type == en_type &&
+	  en->field_type == fltype &&
+	  ((!en->name && !name)
+	   || (en->name && name && !strcmp (en->name, name))))
+	{
+	  if (name)
+	    free (name);
+
+	  return enumtype;
+	}
+
+      last_entry = enumtype;
+      enumtype = enumtype->next2;
+    }
+
+  enumtype =
+    (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+				 sizeof (struct pdb_enum));
+  enumtype->cv_type = LF_ENUM;
+  enumtype->tree = t;
+  enumtype->next = enumtype->next2 = NULL;
+  enumtype->id = 0;
+
+  en = (struct pdb_enum *) enumtype->data;
+  en->count = num_entries;
+  en->field_type = fltype;
+  en->type = en_type;
+  en->name = name;
+
+  if (last_entry)
+    last_entry->next2 = enumtype;
+  else
+    enum_types = enumtype;
+
+  if (last_type)
+    last_type->next = enumtype;
+  else
+    types = enumtype;
+
+  last_type = enumtype;
+
+  slot =
+    tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t), INSERT);
+  *slot = enumtype;
+
+  return enumtype;
+}
+
 /* Given a pointer type t, allocate a new pdb_type and add it to the
  * type list. */
 static struct pdb_type *
@@ -1837,6 +2283,9 @@  find_type (tree t)
     case ARRAY_TYPE:
       return find_type_array (t);
 
+    case ENUMERAL_TYPE:
+      return find_type_enum (t);
+
     case FUNCTION_TYPE:
     case METHOD_TYPE:
       return find_type_function (t);
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 3e5ef8ca1a7..85ce1f68ee5 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -36,7 +36,10 @@ 
 #define S_DEFRANGE_REGISTER		0x1141
 #define S_DEFRANGE_REGISTER_REL		0x1145
 #define LF_ARGLIST			0x1201
+#define LF_FIELDLIST			0x1203
+#define LF_ENUMERATE			0x1502
 #define LF_ARRAY			0x1503
+#define LF_ENUM				0x1507
 #define LF_CHAR				0x8000
 #define LF_SHORT			0x8001
 #define LF_USHORT			0x8002
@@ -134,6 +137,46 @@  struct pdb_global_var
   struct pdb_type *type;
 };
 
+// CV_fldattr_t in cvinfo
+#define CV_FLDATTR_PRIVATE	0x0001
+#define CV_FLDATTR_PROTECTED	0x0002
+#define CV_FLDATTR_PUBLIC	0x0003
+#define CV_FLDATTR_VIRTUAL	0x0004
+#define CV_FLDATTR_STATIC	0x0008
+#define CV_FLDATTR_FRIEND	0x000C
+#define CV_FLDATTR_INTRO	0x0010
+#define CV_FLDATTR_PUREVIRT	0x0014
+#define CV_FLDATTR_PUREINTRO	0x0018
+#define CV_FLDATTR_PSEUDO	0x0020
+#define CV_FLDATTR_NOINHERIT	0x0040
+#define CV_FLDATTR_NOCONSTRUCT	0x0080
+#define CV_FLDATTR_COMPGENX	0x0100
+#define CV_FLDATTR_SEALED	0x0200
+
+struct pdb_fieldlist_entry
+{
+  uint16_t cv_type;
+  struct pdb_type *type;
+  uint16_t offset;
+  uint16_t fld_attr;
+  int64_t value;
+  char *name;
+};
+
+struct pdb_fieldlist
+{
+  unsigned int count;
+  struct pdb_fieldlist_entry entries[1];
+};
+
+struct pdb_enum
+{
+  unsigned int count;
+  struct pdb_type *type;
+  struct pdb_type *field_type;
+  char *name;
+};
+
 // from CV_ptrtype_e in cvdump
 #define CV_PTR_NEAR32		0x0a
 #define CV_PTR_64		0x0c