diff mbox series

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

Message ID 20210320162652.23346-22-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 | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 gcc/pdbout.h |   1 +
 2 files changed, 151 insertions(+), 1 deletion(-)
diff mbox series

Patch

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 0xffff. 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 0xffff, 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 <= 0xffff)
+		    {
+		      delta += 2;	// LF_USHORT
+		    }
+		  else if (fl->entries[i].value >= -0x7fffffff &&
+			  fl->entries[i].value <= 0x7fffffff)
+		    {
+		      delta += 4;	// LF_LONG
+		    }
+		  else if (fl->entries[i].value >= 0x80000000 &&
+			  fl->entries[i].value <= 0xffffffff)
+		    {
+		      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_entries;
+
+	      memcpy (fl2->entries, &fl->entries[i + 1],
+		      sizeof (struct pdb_fieldlist_entry) * num_entries);
+
+	      fl->entries[i + 1].cv_type = LF_INDEX;
+	      fl->entries[i + 1].type = t2;
+	      fl->entries[i + 1].name = NULL;
+
+	      fl->count = i + 2;
+
+	      made_split = true;
+	      prev = t2;
+
+	      break;
+	    }
+
+	  len += delta;
+      }
+
+      if (made_split) // might need to be split again
+	continue;
+
+      prev = t;
+      t = t->next;
+    }
+}
+
 /* We've finished compilation - output the .debug$S and .debug$T sections
  * to the asm file. */
 static void
@@ -1760,6 +1905,8 @@  pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
 {
   mark_referenced_types_used ();
 
+  split_large_fieldlists ();
+
   number_types ();
 
   write_pdb_section ();
@@ -2494,6 +2641,8 @@  add_type_fieldlist (struct pdb_type *t)
   else
     fieldlist_types = t;
 
+  last_fieldlist_type = t;
+
   if (last_type)
     last_type->next = t;
   else
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 5390fdb0f95..f1b21fe23a0 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -38,6 +38,7 @@ 
 #define LF_ARGLIST			0x1201
 #define LF_FIELDLIST			0x1203
 #define LF_BITFIELD			0x1205
+#define LF_INDEX			0x1404
 #define LF_ENUMERATE			0x1502
 #define LF_ARRAY			0x1503
 #define LF_CLASS			0x1504