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_hasher> alias_hash_table (31);
+static hash_table <struct_hasher> 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_fieldlist ((struct pdb_fieldlist *) t->data);
       break;
 
+    case LF_CLASS:
+    case LF_STRUCTURE:
+      write_struct (t->cv_type, (struct pdb_struct *) t->data);
+      break;
+
     case LF_ENUM:
       write_enum ((struct pdb_enum *) t->data);
       break;
@@ -1326,6 +1428,13 @@ get_tree_name (tree t)
 
   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] != '.')
+    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;
 
@@ -1396,7 +1505,73 @@ pdbout_late_global_decl (tree var)
   global_vars = v;
 }
 
-/* Add a fieldlist, which is the basis of enums. */
+/* 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. */
+static struct pdb_type *
+add_struct_forward_declaration (tree t, const char *name)
+{
+  struct pdb_type *strtype;
+  struct pdb_struct *str;
+
+  if (name)
+    {
+      strtype =
+	struct_hash_table.find_with_hash (name, struct_hasher::hash (name));
+
+      if (strtype)
+	return strtype;
+    }
+
+  strtype =
+    (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+				 sizeof (struct pdb_struct));
+
+  if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
+    strtype->cv_type = LF_CLASS;
+  else
+    strtype->cv_type = LF_STRUCTURE;
+
+  strtype->tree = NULL;
+  strtype->next = strtype->next2 = NULL;
+  strtype->id = 0;
+
+  str = (struct pdb_struct *) strtype->data;
+  str->count = 0;
+  str->field_type = NULL;
+  str->size = 0;
+  str->property.value = 0;
+  str->property.s.fwdref = 1;
+  str->name = name ? xstrdup (name) : NULL;
+
+  if (last_struct_type)
+    last_struct_type->next2 = strtype;
+  else
+    struct_types = strtype;
+
+  last_struct_type = strtype;
+
+  if (last_type)
+    last_type->next = strtype;
+  else
+    types = strtype;
+
+  last_type = strtype;
+
+  if (name)
+    {
+      struct pdb_type **slot =
+	struct_hash_table.find_slot_with_hash (name,
+					       struct_hasher::hash (name),
+					       INSERT);
+      *slot = strtype;
+    }
+
+  return strtype;
+}
+
+/* Add a fieldlist - this type does double duty as the basis
+ * of both enums and structs. */
 static struct pdb_type *
 add_type_fieldlist (struct pdb_type *t)
 {
@@ -1425,7 +1600,20 @@ add_type_fieldlist (struct pdb_type *t)
 		  break;
 		}
 
-	      if (pfe1->cv_type == LF_ENUMERATE)
+	      if (pfe1->cv_type == LF_MEMBER)
+		{
+		  if (pfe1->type != pfe2->type ||
+		      pfe1->offset != pfe2->offset ||
+		      pfe1->fld_attr != pfe2->fld_attr ||
+		      ((pfe1->name || pfe2->name) &&
+		       (!pfe1->name || !pfe2->name ||
+			strcmp (pfe1->name, pfe2->name))))
+		    {
+		      same = false;
+		      break;
+		    }
+		}
+	      else if (pfe1->cv_type == LF_ENUMERATE)
 		{
 		  if (pfe1->value != pfe2->value ||
 		      ((pfe1->name || pfe2->name) &&
@@ -1477,6 +1665,278 @@ add_type_fieldlist (struct pdb_type *t)
   return t;
 }
 
+inline hashval_t
+struct_hasher::hash (struct_hasher::compare_type name)
+{
+  return htab_hash_string (name);
+}
+
+inline bool
+struct_hasher::equal (const value_type type, compare_type name)
+{
+  struct pdb_struct *str = (struct pdb_struct *) type->data;
+
+  return !strcmp (str->name, name);
+}
+
+/* For a given struct or class, allocate a new pdb_type and
+ * add it to the type list. */
+static struct pdb_type *
+find_type_struct (tree t)
+{
+  tree f;
+  struct pdb_type *fltype = NULL, *strtype, *fwddef = NULL,
+		  *last_entry = NULL;
+  struct pdb_fieldlist *fieldlist;
+  struct pdb_fieldlist_entry *ent;
+  struct pdb_struct *str;
+  unsigned int num_entries = 0;
+  bool new_fwddef = false;
+  char *name = get_tree_name (t);
+  uint16_t size =
+    TYPE_SIZE (t) ? (TREE_INT_CST_ELT (TYPE_SIZE (t), 0) / 8) : 0;
+  union pdb_property prop;
+  struct pdb_type **slot;
+
+  if (name)
+    {
+      strtype =
+	struct_hash_table.find_with_hash (name, struct_hasher::hash (name));
+
+      /* Use type found in hash map, unless this is a substantive definition
+       * replacing a forward declaration. */
+
+      if (strtype)
+	{
+	  str = (struct pdb_struct *) strtype->data;
+
+	  if (TYPE_SIZE (t) == 0 || str->property.s.fwdref != 1)
+	    return strtype;
+	}
+    }
+
+  f = TYPE_FIELDS (t);
+
+  while (f)
+    {
+      if (TREE_CODE (f) == FIELD_DECL && DECL_FIELD_OFFSET (f))
+	{
+	  if (DECL_NAME (f) && IDENTIFIER_POINTER (DECL_NAME (f)))
+	    num_entries++;
+	  else
+	    {			// anonymous field
+	      struct pdb_type *type = find_type (TREE_TYPE (f));
+
+	      if (type
+		  && (type->cv_type == LF_CLASS
+		      || type->cv_type == LF_STRUCTURE))
+		{
+		  struct pdb_struct *str2 = (struct pdb_struct *) type->data;
+
+		  if (str2->field_type)
+		    {
+		      struct pdb_fieldlist *fl =
+			(struct pdb_fieldlist *) str2->field_type->data;
+
+		      // count fields of anonymous struct or union as our own
+
+		      num_entries += fl->count;
+		    }
+		}
+	    }
+	}
+
+      f = TREE_CHAIN (f);
+    }
+
+  if (TYPE_SIZE (t) != 0)	// not forward declaration
+    {
+      fwddef = add_struct_forward_declaration (t, name);
+
+      if (!fwddef->tree)
+	{
+	  new_fwddef = true;
+	  fwddef->tree = t;
+
+	  slot =
+	    tree_hash_table.find_slot_with_hash (t,
+						 pdb_type_tree_hasher::
+						 hash (t), INSERT);
+	  *slot = fwddef;
+	}
+    }
+
+  if (num_entries > 0)
+    {
+      // 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;
+      f = TYPE_FIELDS (t);
+
+      while (f)
+	{
+	  if (TREE_CODE (f) == FIELD_DECL && DECL_FIELD_OFFSET (f))
+	    {
+	      unsigned int bit_offset =
+		(TREE_INT_CST_ELT (DECL_FIELD_OFFSET (f), 0) * 8) +
+		TREE_INT_CST_ELT (DECL_FIELD_BIT_OFFSET (f), 0);
+
+	      if (DECL_NAME (f) && IDENTIFIER_POINTER (DECL_NAME (f)))
+		{
+
+		  ent->cv_type = LF_MEMBER;
+		  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;
+
+		  ent++;
+		}
+	      else		// anonymous field
+		{
+		  struct pdb_type *type = find_type (TREE_TYPE (f));
+
+		  if (type
+		      && (type->cv_type == LF_CLASS
+			  || type->cv_type == LF_STRUCTURE))
+		    {
+		      struct pdb_struct *str2 =
+			(struct pdb_struct *) type->data;
+
+		      if (str2->field_type)
+			{
+			  struct pdb_fieldlist *fl =
+			    (struct pdb_fieldlist *) str2->field_type->data;
+
+			  // treat fields of anonymous struct or union
+			  // as our own
+
+			  for (unsigned int i = 0; i < fl->count; i++)
+			    {
+			      ent->cv_type = fl->entries[i].cv_type;
+			      ent->type = fl->entries[i].type;
+			      ent->offset =
+				(bit_offset / 8) + fl->entries[i].offset;
+			      ent->fld_attr = fl->entries[i].fld_attr;
+			      ent->name =
+				fl->entries[i].name ?
+				xstrdup (fl->entries[i].name) : NULL;
+
+			      ent++;
+			    }
+			}
+		    }
+		}
+	    }
+
+	  f = TREE_CHAIN (f);
+	}
+
+      fltype = add_type_fieldlist (fltype);
+    }
+
+  // add type for struct
+
+  prop.value = 0;
+
+  if (!TYPE_SIZE (t))		// forward declaration
+    prop.s.fwdref = 1;
+
+  if (!name)
+    {
+      strtype = struct_types;
+      while (strtype)
+	{
+	  str = (struct pdb_struct *) strtype->data;
+
+	  if (str->count == num_entries &&
+	      str->field_type == fltype &&
+	      str->size == size &&
+	      str->property.value == prop.value &&
+	      !str->name)
+	    return strtype;
+
+	  last_entry = strtype;
+	  strtype = strtype->next2;
+	}
+    }
+  else
+    last_entry = last_struct_type;
+
+  strtype =
+    (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+				 sizeof (struct pdb_struct));
+
+  if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
+    strtype->cv_type = LF_CLASS;
+  else
+    strtype->cv_type = LF_STRUCTURE;
+
+  if (TYPE_SIZE (t) != 0)	// not forward declaration
+    strtype->tree = t;
+  else
+    strtype->tree = NULL;
+
+  strtype->next = strtype->next2 = NULL;
+  strtype->id = 0;
+
+  str = (struct pdb_struct *) strtype->data;
+  str->count = num_entries;
+  str->field_type = fltype;
+  str->size = size;
+  str->property.value = prop.value;
+  str->name = name;
+
+  if (last_entry)
+    last_entry->next2 = strtype;
+  else
+    struct_types = strtype;
+
+  last_struct_type = strtype;
+
+  if (last_type)
+    last_type->next = strtype;
+  else
+    types = strtype;
+
+  last_type = strtype;
+
+  if (new_fwddef)
+    fwddef->tree = NULL;
+
+  if (strtype->tree)
+    {
+      slot =
+	tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t),
+					     INSERT);
+      *slot = strtype;
+    }
+
+  if (name)
+    {
+      slot =
+	struct_hash_table.find_slot_with_hash (name,
+					       struct_hasher::hash (name),
+					       INSERT);
+      *slot = strtype;
+    }
+
+  return strtype;
+}
+
 /* Given an array type t, allocate a new pdb_type and add it to the
  * type list. */
 static struct pdb_type *
@@ -2283,6 +2743,9 @@ find_type (tree t)
     case ARRAY_TYPE:
       return find_type_array (t);
 
+    case RECORD_TYPE:
+      return find_type_struct (t);
+
     case ENUMERAL_TYPE:
       return find_type_enum (t);
 
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 85ce1f68ee5..96288d235ea 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -39,7 +39,10 @@
 #define LF_FIELDLIST			0x1203
 #define LF_ENUMERATE			0x1502
 #define LF_ARRAY			0x1503
+#define LF_CLASS			0x1504
+#define LF_STRUCTURE			0x1505
 #define LF_ENUM				0x1507
+#define LF_MEMBER			0x150d
 #define LF_CHAR				0x8000
 #define LF_SHORT			0x8001
 #define LF_USHORT			0x8002
@@ -169,6 +172,37 @@ struct pdb_fieldlist
   struct pdb_fieldlist_entry entries[1];
 };
 
+union pdb_property
+{				// CV_prop_t in cvdump
+  struct
+  {
+    uint16_t packed:1;
+    uint16_t ctor:1;
+    uint16_t ovlops:1;
+    uint16_t isnested:1;
+    uint16_t cnested:1;
+    uint16_t opassign:1;
+    uint16_t opcast:1;
+    uint16_t fwdref:1;
+    uint16_t scoped:1;
+    uint16_t hasuniquename:1;
+    uint16_t sealed:1;
+    uint16_t hfa:2;
+    uint16_t intrinsic:1;
+    uint16_t mocom:2;
+  } s;
+  uint16_t value;
+};
+
+struct pdb_struct
+{
+  unsigned int count;
+  struct pdb_type *field_type;
+  uint16_t size;
+  union pdb_property property;
+  char *name;
+};
+
 struct pdb_enum
 {
   unsigned int count;
@@ -1287,4 +1321,21 @@ struct alias_hasher : nofree_ptr_hash <struct pdb_alias>
   static inline bool equal (const value_type, compare_type);
 };
 
+struct struct_hasher : nofree_ptr_hash <struct pdb_type>
+{
+  typedef struct pdb_type *value_type;
+  typedef const char *compare_type;
+
+  static inline hashval_t hash (compare_type);
+
+  static inline hashval_t hash (const value_type t)
+  {
+    struct pdb_struct *str = (struct pdb_struct *) t->data;
+
+    return hash (str->name);
+  }
+
+  static inline bool equal (const value_type, compare_type);
+};
+
 #endif
