[RFA,08/15] Remove readin and compunit_symtab fields from psymtab

Message ID 20180510222357.27332-9-tom@tromey.com
State New, archived
Headers

Commit Message

Tom Tromey May 10, 2018, 10:23 p.m. UTC
  In order to move psymtabs off of the objfile obstack, there must be no
backlinks from the psymtab (or associated objects) to anything
allocated on the objfile obstack.

This patch removes the readin and compunit_symtab fields from psymtab
to help achieve this goal, replacing them with per-objfile maps
indicating which psymtabs have been read in.  This split will allow
for psymtab reuse across objfiles in the future.

2018-05-10  Tom Tromey  <tom@tromey.com>

	* symfile.c (reread_symbols): Clear psymtab_map.
	* xcoffread.c (read_xcoff_symtab, xcoff_psymtab_to_symtab_1)
	(xcoff_read_symtab): Update.
	* psymtab.h (associate_psymtab_with_symtab, psymtab_read_in_p)
	(get_psymtab_compunit): Declare.
	* psymtab.c (partial_map_expand_apply)
	(psym_find_pc_sect_compunit_symtab, psym_lookup_symbol)
	(psymtab_to_symtab, psym_find_last_source_symtab, dump_psymtab)
	(psym_print_stats, psym_expand_symtabs_for_function)
	(psym_map_symbol_filenames, psym_map_matching_symbols)
	(psym_expand_symtabs_matching, allocate_psymtab): Update.
	(associate_psymtab_with_symtab, psymtab_read_in_p)
	(get_psymtab_compunit): New functions.
	(maintenance_info_psymtabs, maintenance_check_psymtabs): Update.
	* psympriv.h (struct partial_symtab) <readin, compunit_symtab>:
	Remove.
	* objfiles.h (objfile::psymtab_map): New member.
	* mdebugread.c (psymtab_to_symtab_1): Update.
	* dwarf2read.c (dw2_do_instantiate_symtab, dwarf2_read_symtab)
	(process_queue): Update.
	(psymtab_to_symtab_1): Add 'objfile' parameter.
	(get_compunit_symtab, process_full_comp_unit)
	(process_full_type_unit): Update.
	* dbxread.c (dbx_psymtab_to_symtab_1, read_ofile_symtab): Update.
---
 gdb/ChangeLog    | 27 +++++++++++++++++++
 gdb/dbxread.c    | 13 ++++-----
 gdb/dwarf2read.c | 38 +++++++++++++++------------
 gdb/mdebugread.c |  8 +++---
 gdb/objfiles.h   |  6 +++++
 gdb/psympriv.h   | 11 --------
 gdb/psymtab.c    | 80 ++++++++++++++++++++++++++++++++++++++++----------------
 gdb/psymtab.h    | 27 +++++++++++++++++++
 gdb/symfile.c    |  1 +
 gdb/xcoffread.c  | 16 ++++++------
 10 files changed, 159 insertions(+), 68 deletions(-)
  

Comments

Simon Marchi July 18, 2018, 3:34 a.m. UTC | #1
On 2018-05-10 06:23 PM, Tom Tromey wrote:
> In order to move psymtabs off of the objfile obstack, there must be no
> backlinks from the psymtab (or associated objects) to anything
> allocated on the objfile obstack.
> 
> This patch removes the readin and compunit_symtab fields from psymtab
> to help achieve this goal, replacing them with per-objfile maps
> indicating which psymtabs have been read in.  This split will allow
> for psymtab reuse across objfiles in the future.

This LGTM, but I have two question.  In the future, is the intent to also
make symtabs (compunit_symtab) independent from program spaces, and
shareable across objfiles, or would that not be possible?  If it is
possible, would the compunit_symtab pointer in psymtab re-appear, since
there would now again be a single compunit_symtab for each psymtab?

As Pedro pointed out a few times, unordered_map is an hash map with open
hashing.  So we change what was previously a single pointer dereference
to a lookup in a hash table, followed with a linear search in a linked
list.  If this map is looked up very frequently, maybe we should consider
using an htab_t instead?  Some profiling data would help, but it seems
like psymtab_read_in_p is called quite a bit when looking up symbols...

Simon
  
Tom Tromey July 18, 2018, 6:55 p.m. UTC | #2
>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:

>> In order to move psymtabs off of the objfile obstack, there must be no
>> backlinks from the psymtab (or associated objects) to anything
>> allocated on the objfile obstack.

>> This patch removes the readin and compunit_symtab fields from psymtab
>> to help achieve this goal, replacing them with per-objfile maps
>> indicating which psymtabs have been read in.  This split will allow
>> for psymtab reuse across objfiles in the future.

Simon> This LGTM, but I have two question.  In the future, is the intent to also
Simon> make symtabs (compunit_symtab) independent from program spaces, and
Simon> shareable across objfiles, or would that not be possible?  If it is
Simon> possible, would the compunit_symtab pointer in psymtab re-appear, since
Simon> there would now again be a single compunit_symtab for each psymtab?

I think it is a good long-term goal, but not the only possible one in
this area.

The main downside is that it is hard to do.  There's a reasonably
complete to-do list here:

    https://sourceware.org/gdb/wiki/ObjfileSplitting

Some of these steps are tricky and/or laborious.

So, my thought with this second series was that perhaps gdb could get
most of the benefits of the split without the full implementation.

To my mind the performance issue is the major one to solve, with memory
use being second.  So, for instance, if gdb had this psymbol series,
plus maybe incremental expansion of partial symbol tables, perhaps that
would be enough.  This is based in my belief that psymtab reading is the
obvious spot where gdb spends its time, and that CU expansion is
generally fast (but with the occasional counterexample).

So maybe the full objfile splitting project will not even happen (and so
the backlink would never be restored).

Simon> As Pedro pointed out a few times, unordered_map is an hash map with open
Simon> hashing.  So we change what was previously a single pointer dereference
Simon> to a lookup in a hash table, followed with a linear search in a linked
Simon> list.  If this map is looked up very frequently, maybe we should consider
Simon> using an htab_t instead?  Some profiling data would help, but it seems
Simon> like psymtab_read_in_p is called quite a bit when looking up symbols...

I will look into it a bit.

Tom
  
Tom Tromey Sept. 28, 2018, 5:01 a.m. UTC | #3
>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:

Simon> As Pedro pointed out a few times, unordered_map is an hash map with open
Simon> hashing.  So we change what was previously a single pointer dereference
Simon> to a lookup in a hash table, followed with a linear search in a linked
Simon> list.  If this map is looked up very frequently, maybe we should consider
Simon> using an htab_t instead?  Some profiling data would help, but it seems
Simon> like psymtab_read_in_p is called quite a bit when looking up symbols...

I went ahead and switched this to an htab_t.

I considered, a little, porting over the GCC C++ hash table.
I wasn't sure where to put it, though.  I suppose we'd need a new
library.  Also, the GCC one has some GCC-isms (the GC stuff), plus the
unusual "empty" method Pedro has mentioned here before.


I still haven't tried any performance measurements.
I'm not quite sure what to try.

If it performs poorly, maybe a single element cache would help.

Also I should probably look to see if there are any spots that iterate
over psymtabs that could be changed to iterate over the map directly.

I'm sort of considering dropping just this one patch and trying to get
the rest in.

Tom
  
Tom Tromey Oct. 6, 2018, 1:24 a.m. UTC | #4
>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:
Simon> As Pedro pointed out a few times, unordered_map is an hash map with open
Simon> hashing.  So we change what was previously a single pointer dereference
Simon> to a lookup in a hash table, followed with a linear search in a linked
Simon> list.  If this map is looked up very frequently, maybe we should consider
Simon> using an htab_t instead?  Some profiling data would help, but it seems
Simon> like psymtab_read_in_p is called quite a bit when looking up symbols...

[...]

Tom> I still haven't tried any performance measurements.
Tom> I'm not quite sure what to try.

I ran the gdb.perf tests and on some tests, it seems that the results
are within the noise; but on backtrace (2048) and skip-function-3000 (4000)
gdb is about 4% slower.  I don't know if this is enough to worry about.

Tom
  
Simon Marchi Oct. 7, 2018, 10:04 p.m. UTC | #5
On 2018-10-05 9:24 p.m., Tom Tromey wrote:
>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
> 
>>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:
> Simon> As Pedro pointed out a few times, unordered_map is an hash map with open
> Simon> hashing.  So we change what was previously a single pointer dereference
> Simon> to a lookup in a hash table, followed with a linear search in a linked
> Simon> list.  If this map is looked up very frequently, maybe we should consider
> Simon> using an htab_t instead?  Some profiling data would help, but it seems
> Simon> like psymtab_read_in_p is called quite a bit when looking up symbols...
> 
> [...]
> 
> Tom> I still haven't tried any performance measurements.
> Tom> I'm not quite sure what to try.
> 
> I ran the gdb.perf tests and on some tests, it seems that the results
> are within the noise; but on backtrace (2048) and skip-function-3000 (4000)
> gdb is about 4% slower.  I don't know if this is enough to worry about.
> 
> Tom
> 

That sounds like a reasonable trade-off in exchange for the feature of decoupling
psymtabs from objfiles.  Perhaps we can put it on the list of "candidates for htab_t"
(could be a comment next to the field), for when we finally bring the C++ wrappers
from gcc :).

Simon
  
Tom Tromey Oct. 8, 2018, 12:01 a.m. UTC | #6
>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:

>> I ran the gdb.perf tests and on some tests, it seems that the results
>> are within the noise; but on backtrace (2048) and skip-function-3000 (4000)
>> gdb is about 4% slower.  I don't know if this is enough to worry about.

Simon> That sounds like a reasonable trade-off in exchange for the feature of decoupling
Simon> psymtabs from objfiles.  Perhaps we can put it on the list of "candidates for htab_t"
Simon> (could be a comment next to the field), for when we finally bring the C++ wrappers
Simon> from gcc :).

Just to be clear, 4% was the slowdown after I switched it to htab_t.

Tom
  

Patch

diff --git a/gdb/dbxread.c b/gdb/dbxread.c
index 17d53331a4..c3741fda8a 100644
--- a/gdb/dbxread.c
+++ b/gdb/dbxread.c
@@ -2149,7 +2149,7 @@  dbx_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
 {
   int i;
 
-  if (pst->readin)
+  if (psymtab_read_in_p (objfile, pst))
     {
       fprintf_unfiltered (gdb_stderr, "Psymtab for %s already read in.  "
 			  "Shouldn't happen.\n",
@@ -2159,7 +2159,7 @@  dbx_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
 
   /* Read in all partial symtabs on which this one is dependent.  */
   for (i = 0; i < pst->number_of_dependencies; i++)
-    if (!pst->dependencies[i]->readin)
+    if (!psymtab_read_in_p (objfile, pst->dependencies[i]))
       {
 	/* Inform about additional files that need to be read in.  */
 	if (info_verbose)
@@ -2189,7 +2189,7 @@  dbx_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
       read_ofile_symtab (objfile, pst);
     }
 
-  pst->readin = 1;
+  associate_psymtab_with_symtab (objfile, pst, nullptr, false);
 }
 
 /* Read in all of the symbols for a given psymtab for real.
@@ -2198,7 +2198,7 @@  dbx_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
 static void
 dbx_read_symtab (struct partial_symtab *self, struct objfile *objfile)
 {
-  if (self->readin)
+  if (psymtab_read_in_p (objfile, self))
     {
       fprintf_unfiltered (gdb_stderr, "Psymtab for %s already read in.  "
 			  "Shouldn't happen.\n",
@@ -2406,8 +2406,9 @@  read_ofile_symtab (struct objfile *objfile, struct partial_symtab *pst)
   if (last_source_start_addr > text_offset)
     last_source_start_addr = text_offset;
 
-  pst->compunit_symtab = end_symtab (text_offset + text_size,
-				     SECT_OFF_TEXT (objfile));
+  associate_psymtab_with_symtab (objfile, pst,
+				 end_symtab (text_offset + text_size,
+					     SECT_OFF_TEXT (objfile)));
 
   end_stabs ();
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 9e846e75bb..bf6e4b865a 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -1458,7 +1458,7 @@  static void add_partial_subprogram (struct partial_die_info *pdi,
 static void dwarf2_read_symtab (struct partial_symtab *,
 				struct objfile *);
 
-static void psymtab_to_symtab_1 (struct partial_symtab *);
+static void psymtab_to_symtab_1 (struct objfile *, struct partial_symtab *);
 
 static abbrev_table_up abbrev_table_read_table
   (struct dwarf2_per_objfile *dwarf2_per_objfile, struct dwarf2_section_info *,
@@ -2867,7 +2867,9 @@  dw2_do_instantiate_symtab (struct dwarf2_per_cu_data *per_cu)
 
   if (dwarf2_per_objfile->using_index
       ? per_cu->v.quick->compunit_symtab == NULL
-      : (per_cu->v.psymtab == NULL || !per_cu->v.psymtab->readin))
+      : (per_cu->v.psymtab == NULL
+	 || !psymtab_read_in_p (dwarf2_per_objfile->objfile,
+				per_cu->v.psymtab)))
     {
       queue_comp_unit (per_cu, language_minimal);
       load_cu (per_cu);
@@ -9330,7 +9332,7 @@  dwarf2_read_symtab (struct partial_symtab *self,
   struct dwarf2_per_objfile *dwarf2_per_objfile
     = get_dwarf2_per_objfile (objfile);
 
-  if (self->readin)
+  if (psymtab_read_in_p (objfile, self))
     {
       warning (_("bug: psymtab for %s is already read in."),
 	       self->filename);
@@ -9359,7 +9361,7 @@  dwarf2_read_symtab (struct partial_symtab *self,
 
       dwarf2_per_objfile->reading_partial_symbols = 0;
 
-      psymtab_to_symtab_1 (self);
+      psymtab_to_symtab_1 (objfile, self);
 
       /* Finish up the debug error message.  */
       if (info_verbose)
@@ -9460,7 +9462,9 @@  process_queue (struct dwarf2_per_objfile *dwarf2_per_objfile)
     {
       if ((dwarf2_per_objfile->using_index
 	   ? !item->per_cu->v.quick->compunit_symtab
-	   : (item->per_cu->v.psymtab && !item->per_cu->v.psymtab->readin))
+	   : (item->per_cu->v.psymtab
+	      && !psymtab_read_in_p (dwarf2_per_objfile->objfile,
+				     item->per_cu->v.psymtab)))
 	  /* Skip dummy CUs.  */
 	  && item->per_cu->cu != NULL)
 	{
@@ -9516,16 +9520,16 @@  process_queue (struct dwarf2_per_objfile *dwarf2_per_objfile)
 /* Read in full symbols for PST, and anything it depends on.  */
 
 static void
-psymtab_to_symtab_1 (struct partial_symtab *pst)
+psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
 {
   struct dwarf2_per_cu_data *per_cu;
   int i;
 
-  if (pst->readin)
+  if (psymtab_read_in_p (objfile, pst))
     return;
 
   for (i = 0; i < pst->number_of_dependencies; i++)
-    if (!pst->dependencies[i]->readin
+    if (!psymtab_read_in_p (objfile, pst->dependencies[i])
 	&& pst->dependencies[i]->user == NULL)
       {
         /* Inform about additional files that need to be read in.  */
@@ -9540,7 +9544,7 @@  psymtab_to_symtab_1 (struct partial_symtab *pst)
             wrap_here ("");     /* Flush output.  */
             gdb_flush (gdb_stdout);
           }
-        psymtab_to_symtab_1 (pst->dependencies[i]);
+        psymtab_to_symtab_1 (objfile, pst->dependencies[i]);
       }
 
   per_cu = (struct dwarf2_per_cu_data *) pst->read_symtab_private;
@@ -9549,7 +9553,7 @@  psymtab_to_symtab_1 (struct partial_symtab *pst)
     {
       /* It's an include file, no symbols to read for it.
          Everything is in the parent symtab.  */
-      pst->readin = 1;
+      associate_psymtab_with_symtab (objfile, pst, nullptr);
       return;
     }
 
@@ -10108,9 +10112,11 @@  rust_union_quirks (struct dwarf2_cu *cu)
 static struct compunit_symtab *
 get_compunit_symtab (struct dwarf2_per_cu_data *per_cu)
 {
-  return (per_cu->dwarf2_per_objfile->using_index
-	  ? per_cu->v.quick->compunit_symtab
-	  : per_cu->v.psymtab->compunit_symtab);
+  if (per_cu->dwarf2_per_objfile->using_index)
+    return per_cu->v.quick->compunit_symtab;
+
+  struct objfile *objfile = per_cu->dwarf2_per_objfile->objfile;
+  return get_psymtab_compunit (objfile, per_cu->v.psymtab);
 }
 
 /* A helper function for computing the list of all symbol tables
@@ -10344,8 +10350,7 @@  process_full_comp_unit (struct dwarf2_per_cu_data *per_cu,
   else
     {
       struct partial_symtab *pst = per_cu->v.psymtab;
-      pst->compunit_symtab = cust;
-      pst->readin = 1;
+      associate_psymtab_with_symtab (objfile, pst, cust);
     }
 
   /* Push it for inclusion processing later.  */
@@ -10425,8 +10430,7 @@  process_full_type_unit (struct dwarf2_per_cu_data *per_cu,
   else
     {
       struct partial_symtab *pst = per_cu->v.psymtab;
-      pst->compunit_symtab = cust;
-      pst->readin = 1;
+      associate_psymtab_with_symtab (objfile, pst, cust);
     }
 }
 
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index 29fe3455db..785e9dbfa0 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -3902,16 +3902,16 @@  psymtab_to_symtab_1 (struct objfile *objfile,
   int last_symtab_ended = 0;
   struct section_offsets *section_offsets = objfile->section_offsets;
 
-  if (pst->readin)
+  if (psymtab_read_in_p (objfile, pst))
     return;
-  pst->readin = 1;
+  associate_psymtab_with_symtab (objfile, pst, nullptr, false);
 
   /* Read in all partial symbtabs on which this one is dependent.
      NOTE that we do have circular dependencies, sigh.  We solved
      that by setting pst->readin before this point.  */
 
   for (i = 0; i < pst->number_of_dependencies; i++)
-    if (!pst->dependencies[i]->readin)
+    if (!psymtab_read_in_p (objfile, pst->dependencies[i]))
       {
 	/* Inform about additional files to be read in.  */
 	if (info_verbose)
@@ -4291,7 +4291,7 @@  psymtab_to_symtab_1 (struct objfile *objfile,
     }
 
   /* Now link the psymtab and the symtab.  */
-  pst->compunit_symtab = cust;
+  associate_psymtab_with_symtab (objfile, pst, cust);
 
   mdebugread_objfile = NULL;
 }
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 28e66eca36..225060d22f 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -28,6 +28,7 @@ 
 #include "registry.h"
 #include "gdb_bfd.h"
 #include <vector>
+#include <unordered_map>
 
 struct bcache;
 struct htab;
@@ -372,6 +373,11 @@  struct objfile
   std::vector<partial_symbol *> global_psymbols;
   std::vector<partial_symbol *> static_psymbols;
 
+  /* Map from a psymtab to its corresponding full symtab.  If a
+     psymtab is read in but fails to resolve to a full symtab, this
+     map will hold an entry for the psymtab that maps to nullptr.  */
+  std::unordered_map<partial_symtab *, compunit_symtab *> psymtab_map;
+
   /* Structure which keeps track of functions that manipulate objfile's
      of the same type as this objfile.  I.e. the function to read partial
      symbols for example.  Note that this structure is in statically
diff --git a/gdb/psympriv.h b/gdb/psympriv.h
index 7673eaef39..969215e2f1 100644
--- a/gdb/psympriv.h
+++ b/gdb/psympriv.h
@@ -193,12 +193,6 @@  struct partial_symtab
   int statics_offset;
   int n_static_syms;
 
-  /* Non-zero if the symtab corresponding to this psymtab has been
-     readin.  This is located here so that this structure packs better
-     on 64-bit systems.  */
-
-  unsigned char readin;
-
   /* True iff objfile->psymtabs_addrmap is properly populated for this
      partial_symtab.  For discontiguous overlapping psymtabs is the only usable
      info in PSYMTABS_ADDRMAP.  */
@@ -218,11 +212,6 @@  struct partial_symtab
   unsigned int textlow_valid : 1;
   unsigned int texthigh_valid : 1;
 
-  /* Pointer to compunit eventually allocated for this source file, 0 if
-     !readin or if we haven't looked for the symtab after it was readin.  */
-
-  struct compunit_symtab *compunit_symtab;
-
   /* Pointer to function which will read in the symtab corresponding to
      this psymtab.  */
 
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index a966b39730..f2177c76ee 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -142,7 +142,7 @@  partial_map_expand_apply (struct objfile *objfile,
   gdb_assert (pst->user == NULL);
 
   /* Don't visit already-expanded psymtabs.  */
-  if (pst->readin)
+  if (psymtab_read_in_p (objfile, pst))
     return 0;
 
   /* This may expand more than one symtab, and we want to iterate over
@@ -390,7 +390,7 @@  psym_find_pc_sect_compunit_symtab (struct objfile *objfile,
 						    msymbol);
   if (ps != NULL)
     {
-      if (warn_if_readin && ps->readin)
+      if (warn_if_readin && psymtab_read_in_p (objfile, ps))
 	/* Might want to error() here (in case symtab is corrupt and
 	   will cause a core dump), but maybe we can successfully
 	   continue, so let's not.  */
@@ -398,7 +398,7 @@  psym_find_pc_sect_compunit_symtab (struct objfile *objfile,
 (Internal error: pc %s in read in psymtab, but not in symtab.)\n"),
 		 paddress (get_objfile_arch (objfile), pc));
       psymtab_to_symtab (objfile, ps);
-      return ps->compunit_symtab;
+      return get_psymtab_compunit (objfile, ps);
     }
   return NULL;
 }
@@ -488,8 +488,8 @@  psym_lookup_symbol (struct objfile *objfile,
 
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
   {
-    if (!ps->readin && lookup_partial_symbol (objfile, ps, name,
-					      psymtab_index, domain))
+    if (!psymtab_read_in_p (objfile, ps)
+	&& lookup_partial_symbol (objfile, ps, name, psymtab_index, domain))
       {
 	struct symbol *sym, *with_opaque = NULL;
 	struct compunit_symtab *stab = psymtab_to_symtab (objfile, ps);
@@ -753,19 +753,15 @@  psymtab_to_symtab (struct objfile *objfile, struct partial_symtab *pst)
   while (pst->user != NULL)
     pst = pst->user;
 
-  /* If it's been looked up before, return it.  */
-  if (pst->compunit_symtab)
-    return pst->compunit_symtab;
-
   /* If it has not yet been read in, read it.  */
-  if (!pst->readin)
+  if (!psymtab_read_in_p (objfile, pst))
     {
       scoped_restore decrementer = increment_reading_symtab ();
 
       (*pst->read_symtab) (pst, objfile);
     }
 
-  return pst->compunit_symtab;
+  return get_psymtab_compunit (objfile, pst);
 }
 
 /* Psymtab version of find_last_source_symtab.  See its definition in
@@ -789,7 +785,7 @@  psym_find_last_source_symtab (struct objfile *ofp)
 
   if (cs_pst)
     {
-      if (cs_pst->readin)
+      if (psymtab_read_in_p (ofp, cs_pst))
 	{
 	  internal_error (__FILE__, __LINE__,
 			  _("select_source_symtab: "
@@ -943,11 +939,12 @@  dump_psymtab (struct objfile *objfile, struct partial_symtab *psymtab,
   gdb_print_host_address (objfile, outfile);
   fprintf_unfiltered (outfile, ")\n");
 
-  if (psymtab->readin)
+  if (psymtab_read_in_p (objfile, psymtab))
     {
       fprintf_filtered (outfile,
 			"  Full symtab was read (at ");
-      gdb_print_host_address (psymtab->compunit_symtab, outfile);
+      gdb_print_host_address (get_psymtab_compunit (objfile, psymtab),
+			      outfile);
       fprintf_filtered (outfile, " by function at ");
       gdb_print_host_address (psymtab->read_symtab, outfile);
       fprintf_filtered (outfile, ")\n");
@@ -1004,7 +1001,7 @@  psym_print_stats (struct objfile *objfile)
   i = 0;
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
     {
-      if (ps->readin == 0)
+      if (psymtab_read_in_p (objfile, ps) == 0)
 	i++;
     }
   printf_filtered (_("  Number of psym tables (not yet expanded): %d\n"), i);
@@ -1046,7 +1043,7 @@  psym_expand_symtabs_for_function (struct objfile *objfile,
 
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
   {
-    if (ps->readin)
+    if (psymtab_read_in_p (objfile, ps))
       continue;
 
     if ((lookup_partial_symbol (objfile, ps, func_name, 1, VAR_DOMAIN)
@@ -1109,7 +1106,7 @@  psym_map_symbol_filenames (struct objfile *objfile,
     {
       const char *fullname;
 
-      if (ps->readin)
+      if (psymtab_read_in_p (objfile, ps))
 	continue;
 
       /* We can skip shared psymtabs here, because any file name will be
@@ -1224,7 +1221,7 @@  psym_map_matching_symbols (struct objfile *objfile,
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
     {
       QUIT;
-      if (ps->readin
+      if (psymtab_read_in_p (objfile, ps)
 	  || match_partial_symbol (objfile, ps, global, name, domain, match,
 				   ordered_compare))
 	{
@@ -1356,7 +1353,7 @@  psym_expand_symtabs_matching
     {
       QUIT;
 
-      if (ps->readin)
+      if (psymtab_read_in_p (objfile, ps))
 	continue;
 
       /* We skip shared psymtabs because file-matching doesn't apply
@@ -1762,7 +1759,6 @@  allocate_psymtab (const char *filename, struct objfile *objfile)
   psymtab->filename
     = (const char *) bcache (filename, strlen (filename) + 1,
 			     objfile->per_bfd->filename_cache);
-  psymtab->compunit_symtab = NULL;
 
   /* Prepend it to the psymtab list for the objfile it belongs to.
      Psymtabs are searched in most recent inserted -> least recent
@@ -1821,6 +1817,45 @@  discard_psymtab (struct objfile *objfile, struct partial_symtab *pst)
 
 
 
+/* See psympriv.h.  */
+
+void
+associate_psymtab_with_symtab (struct objfile *objfile,
+			       partial_symtab *pst,
+			       compunit_symtab *symtab,
+			       bool always_set)
+{
+  if (!always_set)
+    {
+      auto iter = objfile->psymtab_map.find (pst);
+      if (iter != objfile->psymtab_map.end ())
+	return;
+    }
+  objfile->psymtab_map[pst] = symtab;
+}
+
+/* See psympriv.h.  */
+
+bool
+psymtab_read_in_p (struct objfile *objfile, partial_symtab *pst)
+{
+  auto iter = objfile->psymtab_map.find (pst);
+  return iter != objfile->psymtab_map.end ();
+}
+
+/* See psympriv.h.  */
+
+compunit_symtab *
+get_psymtab_compunit (struct objfile *objfile, partial_symtab *pst)
+{
+  auto iter = objfile->psymtab_map.find (pst);
+  if (iter == objfile->psymtab_map.end ())
+    return nullptr;
+  return iter->second;
+}
+
+
+
 /* We need to pass a couple of items to the addrmap_foreach function,
    so use a struct.  */
 
@@ -2094,7 +2129,8 @@  maintenance_info_psymtabs (const char *regexp, int from_tty)
 			       host_address_to_string (psymtab));
 
 	      printf_filtered ("    readin %s\n",
-			       psymtab->readin ? "yes" : "no");
+			       psymtab_read_in_p (objfile, psymtab)
+			       ? "yes" : "no");
 	      printf_filtered ("    fullname %s\n",
 			       psymtab->fullname
 			       ? psymtab->fullname : "(null)");
@@ -2181,7 +2217,7 @@  maintenance_check_psymtabs (const char *ignore, int from_tty)
     /* We don't call psymtab_to_symtab here because that may cause symtab
        expansion.  When debugging a problem it helps if checkers leave
        things unchanged.  */
-    cust = ps->compunit_symtab;
+    cust = get_psymtab_compunit (objfile, ps);
 
     /* First do some checks that don't require the associated symtab.  */
     if (PSYMTAB_TEXTHIGH (objfile, ps) < PSYMTAB_TEXTLOW (objfile, ps))
diff --git a/gdb/psymtab.h b/gdb/psymtab.h
index 6d9f257f31..7c5bf80e70 100644
--- a/gdb/psymtab.h
+++ b/gdb/psymtab.h
@@ -24,6 +24,8 @@ 
 
 /* A bcache for partial symbols.  */
 
+struct compunit_symtab;
+struct partial_symtab;
 struct psymbol_bcache;
 
 extern struct psymbol_bcache *psymbol_bcache_init (void);
@@ -43,4 +45,29 @@  extern const struct quick_symbol_functions dwarf2_debug_names_functions;
 extern struct objfile *require_partial_symbols (struct objfile *objfile,
 						int verbose);
 
+/* Record that, for the given objfile, PST has expanded to SYMTAB.
+   SYMTAB may be nullptr, indicating that an attempt to expand PST was
+   made, but yielded no results (perhaps this was an included
+   psymtab).  By default, this association is always made, but if
+   ALWAYS_SET is false, then an association is only made if one has
+   not been made previously.  */
+
+extern void associate_psymtab_with_symtab (struct objfile *objfile,
+					   partial_symtab *pst,
+					   compunit_symtab *symtab,
+					   bool always_set = true);
+
+/* Return true if PST was ever read in for the given objfile, false
+   otherwise.  This only records whether an attempt was made -- not
+   whether it yielded a full symtab.  */
+
+extern bool psymtab_read_in_p (struct objfile *objfile, partial_symtab *pst);
+
+/* Return the full symtab corresponding to PST.  Returns NULL if the
+   partial symtab was never read, or if the attempt to read it yielded
+   no results.  */
+
+extern compunit_symtab *get_psymtab_compunit (struct objfile *objfile,
+					      partial_symtab *pst);
+
 #endif /* PSYMTAB_H */
diff --git a/gdb/symfile.c b/gdb/symfile.c
index b0a5f11834..8a63fe34d3 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -2399,6 +2399,7 @@  reread_symbols (void)
 	     enough?  */
 	  objfile->global_psymbols.clear ();
 	  objfile->static_psymbols.clear ();
+	  objfile->psymtab_map.clear ();
 
 	  /* Free the obstacks for non-reusable objfiles.  */
 	  psymbol_bcache_free (objfile->psymbol_cache);
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index 7ec367a7ac..218cfaef5e 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -1134,8 +1134,9 @@  read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 	{
 	  if (get_last_source_file ())
 	    {
-	      pst->compunit_symtab = end_symtab (cur_src_end_addr,
-						 SECT_OFF_TEXT (objfile));
+	      associate_psymtab_with_symtab
+		(objfile, pst, end_symtab (cur_src_end_addr,
+					   SECT_OFF_TEXT (objfile)));
 	      end_stabs ();
 	    }
 
@@ -1530,8 +1531,7 @@  read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
          to make sure that we set pst->compunit_symtab to the symtab for the
          file, not to the _globals_ symtab.  I'm not sure whether this
          actually works right or when/if it comes up.  */
-      if (pst->compunit_symtab == NULL)
-	pst->compunit_symtab = cust;
+      associate_psymtab_with_symtab (objfile, pst, cust, false);
       end_stabs ();
     }
 }
@@ -1843,7 +1843,7 @@  xcoff_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
   if (!pst)
     return;
 
-  if (pst->readin)
+  if (psymtab_read_in_p (objfile, pst))
     {
       fprintf_unfiltered
 	(gdb_stderr, "Psymtab for %s already read in.  Shouldn't happen.\n",
@@ -1853,7 +1853,7 @@  xcoff_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
 
   /* Read in all partial symtabs on which this one is dependent.  */
   for (i = 0; i < pst->number_of_dependencies; i++)
-    if (!pst->dependencies[i]->readin)
+    if (!psymtab_read_in_p (objfile, pst->dependencies[i]))
       {
 	/* Inform about additional files that need to be read in.  */
 	if (info_verbose)
@@ -1879,7 +1879,7 @@  xcoff_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
       read_xcoff_symtab (objfile, pst);
     }
 
-  pst->readin = 1;
+  associate_psymtab_with_symtab (objfile, pst, nullptr, false);
 }
 
 /* Read in all of the symbols for a given psymtab for real.
@@ -1888,7 +1888,7 @@  xcoff_psymtab_to_symtab_1 (struct objfile *objfile, struct partial_symtab *pst)
 static void
 xcoff_read_symtab (struct partial_symtab *self, struct objfile *objfile)
 {
-  if (self->readin)
+  if (psymtab_read_in_p (objfile, self))
     {
       fprintf_unfiltered
 	(gdb_stderr, "Psymtab for %s already read in.  Shouldn't happen.\n",