---
 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[] = "<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
-- 
2.26.2

Reply via email to