[v2,40/42] Replace dwarf2_per_cu_data::cu backlink with per-objfile map

Message ID 20200512211824.6834-41-simon.marchi@efficios.com
State New
Headers show
Series
  • Share DWARF partial symtabs between objfiles
Related show

Commit Message

H.J. Lu via Gdb-patches May 12, 2020, 9:18 p.m.
The dwarf2_per_cu_data type is going to become objfile-independent,
while the dwarf2_cu type will stay object-dependent.  This patch removes
the backlink from dwarf2_per_cu_data to dwarf2_cu, in favor of the
dwarf2_per_objfile::m_dwarf2_cus map.  It maps dwarf2_per_cu_data
objects to the corresponding dwarf2_cu objects for this objfile.  If a
CU has been read in in the context of this objfile, then an entry will
be present in the map.

The dwarf2_cu objects that are read in are currently kept in a linked
list rooted in the dwarf2_per_bfd.  Except that the dwarf2_cu objects
are not simply linked together, they are interleaved with their
corresponding dwarf2_per_cu_data objects.  So if we have CUs A and B
read in, the dwarf2_per_bfd::read_in_chain will point to a chain like
this (DPCD == dwarf2_per_cu_data, DC == dwarf2_cu):

 DPCD A -> DC A -> DPCD B -> DC B

Obviously, this can't stay as is, since a same CU can be read in for an
objfile but not read in for another objfile sharing the same BFD, and
the dwarf2_per_cu_data::cu link is removed.   This is all replaced by
the dwarf2_per_objfile::m_dwarf2_cus map.

gdb/ChangeLog:

	* dwarf2/read.h (struct dwarf2_cu): Forward-declare.
	(struct dwarf2_per_bfd) <free_cached_comp_units>: Remove,
	move to dwarf2_per_objfile.
	<read_in_chain>: Remove.
	(struct dwarf2_per_objfile) <get_cu, set_cu, remove_cu,
	remove_all_cus, age_comp_units>: New methods.
	<m_dwarf2_cus>: New member.
	(struct dwarf2_per_cu_data) <cu>: Remove.
	* dwarf2/read.c (struct dwarf2_cu) <read_in_chain>: Remove.
	(age_cached_comp_units, free_one_cached_comp_unit): Remove,
	moved to methods of dwarf2_per_objfile.
	(dwarf2_clear_marks): Remove.
	(dwarf2_queue_item::~dwarf2_queue_item): Update.
	(dwarf2_per_bfd::~dwarf2_per_bfd): Don't free dwarf2_cus.
	(dwarf2_per_bfd::free_cached_comp_units): Remove.
	(dwarf2_per_objfile::remove_all_cus): New.
	(class free_cached_comp_units) <~free_cached_comp_units>:
	Update.
	(load_cu): Update.
	(dw2_do_instantiate_symtab): Adjust.
	(fill_in_sig_entry_from_dwo_entry): Adjust.
	(cutu_reader::init_tu_and_read_dwo_dies): Update.
	(cutu_reader::cutu_reader): Likewise.
	(cutu_reader::keep): Use dwarf2_per_objfile::set_cu.
	(cutu_reader::cutu_reader): Use dwarf2_per_objfile::get_cu.
	(process_psymtab_comp_unit): Use dwarf2_per_objfile::remove_cu
	and dwarf2_per_objfile::age_comp_units.
	(load_partial_comp_unit): Update.
	(maybe_queue_comp_unit): Use dwarf2_per_objfile::get_cu.
	(process_queue): Likewise.
	(find_partial_die): Use dwarf2_per_objfile::get_cu instead of cu
	backlink.
	(dwarf2_read_addr_index): Likewise.
	(follow_die_offset): Likewise.
	(dwarf2_fetch_die_loc_sect_off): Likewise.
	(dwarf2_fetch_constant_bytes): Likewise.
	(dwarf2_fetch_die_type_sect_off): Likewise.
	(follow_die_sig_1): Likewise.
	(load_full_type_unit): Likewise.
	(read_signatured_type): Likewise.
	(dwarf2_cu::dwarf2_cu): Don't set cu field.
	(dwarf2_cu::~dwarf2_cu): Remove.
	(dwarf2_per_objfile::get_cu): New.
	(dwarf2_per_objfile::set_cu): New.
	(age_cached_comp_units): Rename to...
	(dwarf2_per_objfile::age_comp_units): ... this.  Adjust
	to std::unordered_map.
	(free_one_cached_comp_unit): Rename to...
	(dwarf2_per_objfile::remove_cu): ... this.  Adjust
	to std::unordered_map.
	(dwarf2_per_objfile::~dwarf2_per_objfile): New.
	(dwarf2_mark_helper): Use dwarf2_per_objfile::get_cu, expect
	a dwarf2_per_objfile in data.
	(dwarf2_mark): Pass dwarf2_per_objfile in data to htab_traverse.
	(dwarf2_clear_marks): Remove.
---
 gdb/dwarf2/read.c | 355 ++++++++++++++++++++++------------------------
 gdb/dwarf2/read.h |  35 +++--
 2 files changed, 189 insertions(+), 201 deletions(-)

-- 
2.26.2

Patch

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index acc148a87d5..9bc19a089b0 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -401,7 +401,6 @@  struct dwarf2_cu
 {
   explicit dwarf2_cu (dwarf2_per_cu_data *per_cu,
 		      dwarf2_per_objfile *per_objfile);
-  ~dwarf2_cu ();
 
   DISABLE_COPY_AND_ASSIGN (dwarf2_cu);
 
@@ -468,12 +467,6 @@  struct dwarf2_cu
      unit, including partial DIEs.  */
   auto_obstack comp_unit_obstack;
 
-  /* When multiple dwarf2_cu structures are living in memory, this field
-     chains them all together, so that they can be released efficiently.
-     We will probably also want a generation counter so that most-recently-used
-     compilation units are cached...  */
-  struct dwarf2_per_cu_data *read_in_chain = nullptr;
-
   /* Backlink to our per_cu entry.  */
   struct dwarf2_per_cu_data *per_cu;
 
@@ -1553,11 +1546,6 @@  static void prepare_one_comp_unit (struct dwarf2_cu *cu,
 				   struct die_info *comp_unit_die,
 				   enum language pretend_language);
 
-static void age_cached_comp_units (struct dwarf2_per_objfile *dwarf2_per_objfile);
-
-static void free_one_cached_comp_unit (dwarf2_per_cu_data *target_per_cu,
-				       dwarf2_per_objfile *per_objfile);
-
 static struct type *set_die_type (struct die_info *, struct type *,
 				  struct dwarf2_cu *);
 
@@ -1581,8 +1569,6 @@  static void dwarf2_add_dependence (struct dwarf2_cu *,
 
 static void dwarf2_mark (struct dwarf2_cu *);
 
-static void dwarf2_clear_marks (struct dwarf2_per_cu_data *);
-
 static struct type *get_die_type_at_offset (sect_offset,
 					    dwarf2_per_cu_data *per_cu,
 					    dwarf2_per_objfile *per_objfile);
@@ -1629,8 +1615,7 @@  dwarf2_queue_item::~dwarf2_queue_item ()
      inconsistent state, so discard it.  */
   if (per_cu->queued)
     {
-      if (per_cu->cu != NULL)
-	free_one_cached_comp_unit (per_cu, per_objfile);
+      per_objfile->remove_cu (per_cu);
       per_cu->queued = 0;
     }
 }
@@ -1772,9 +1757,6 @@  dwarf2_per_bfd::dwarf2_per_bfd (bfd *obfd, const dwarf2_debug_sections *names,
 
 dwarf2_per_bfd::~dwarf2_per_bfd ()
 {
-  /* Cached DIE trees use xmalloc and the comp_unit_obstack.  */
-  free_cached_comp_units ();
-
   for (dwarf2_per_cu_data *per_cu : all_comp_units)
     per_cu->imported_symtabs_free ();
 
@@ -1784,21 +1766,15 @@  dwarf2_per_bfd::~dwarf2_per_bfd ()
   /* Everything else should be on this->obstack.  */
 }
 
-/* See declaration.  */
+/* See read.h.  */
 
 void
-dwarf2_per_bfd::free_cached_comp_units ()
+dwarf2_per_objfile::remove_all_cus ()
 {
-  dwarf2_per_cu_data *per_cu = read_in_chain;
-  dwarf2_per_cu_data **last_chain = &read_in_chain;
-  while (per_cu != NULL)
-    {
-      dwarf2_per_cu_data *next_cu = per_cu->cu->read_in_chain;
+  for (auto pair : m_dwarf2_cus)
+    delete pair.second;
 
-      delete per_cu->cu;
-      *last_chain = next_cu;
-      per_cu = next_cu;
-    }
+  m_dwarf2_cus.clear ();
 }
 
 /* A helper class that calls free_cached_comp_units on
@@ -1815,7 +1791,7 @@  class free_cached_comp_units
 
   ~free_cached_comp_units ()
   {
-    m_per_objfile->per_bfd->free_cached_comp_units ();
+    m_per_objfile->remove_all_cus ();
   }
 
   DISABLE_COPY_AND_ASSIGN (free_cached_comp_units);
@@ -2344,12 +2320,13 @@  load_cu (dwarf2_per_cu_data *per_cu, dwarf2_per_objfile *per_objfile,
   else
     load_full_comp_unit (per_cu, per_objfile, skip_partial, language_minimal);
 
-  if (per_cu->cu == nullptr)
+  dwarf2_cu *cu = per_objfile->get_cu (per_cu);
+  if (cu == nullptr)
     return nullptr;  /* Dummy CU.  */
 
-  dwarf2_find_base_address (per_cu->cu->dies, per_cu->cu);
+  dwarf2_find_base_address (cu->dies, cu);
 
-  return per_cu->cu;
+  return cu;
 }
 
 /* Read in the symbols for PER_CU in the context of DWARF"_PER_OBJFILE.  */
@@ -2391,7 +2368,7 @@  dw2_do_instantiate_symtab (dwarf2_per_cu_data *per_cu,
 
   /* Age the cache, releasing compilation units that have not
      been used recently.  */
-  age_cached_comp_units (dwarf2_per_objfile);
+  dwarf2_per_objfile->age_comp_units ();
 }
 
 /* Ensure that the symbols for PER_CU have been read in.  DWARF2_PER_OBJFILE is
@@ -6427,7 +6404,7 @@  fill_in_sig_entry_from_dwo_entry (struct dwarf2_per_objfile *dwarf2_per_objfile,
 
   /* Make sure we're not clobbering something we don't expect to.  */
   gdb_assert (! sig_entry->per_cu.queued);
-  gdb_assert (sig_entry->per_cu.cu == NULL);
+  gdb_assert (dwarf2_per_objfile->get_cu (&sig_entry->per_cu) == NULL);
   if (per_bfd->using_index)
     {
       gdb_assert (sig_entry->per_cu.v.quick != NULL);
@@ -6885,8 +6862,9 @@  cutu_reader::init_tu_and_read_dwo_dies (dwarf2_per_cu_data *this_cu,
     }
   else
     {
-      /* If and existing_cu is provided, this_cu->cu must be NULL.  */
-      gdb_assert (this_cu->cu == NULL);
+      /* If an existing_cu is provided, a dwarf2_cu must not exist for this_cu
+         in per_objfile yet.  */
+      gdb_assert (per_objfile->get_cu (this_cu) == nullptr);
       m_new_cu.reset (new dwarf2_cu (this_cu, per_objfile));
       cu = m_new_cu.get ();
     }
@@ -6975,8 +6953,9 @@  cutu_reader::cutu_reader (dwarf2_per_cu_data *this_cu,
     }
   else
     {
-      /* If an existing_cu is provided, this_cu->cu must be NULL.  */
-      gdb_assert (this_cu->cu == NULL);
+      /* If an existing_cu is provided, a dwarf2_cu must not exist for this_cu
+         in per_objfile yet.  */
+      gdb_assert (dwarf2_per_objfile->get_cu (this_cu) == nullptr);
       m_new_cu.reset (new dwarf2_cu (this_cu, dwarf2_per_objfile));
       cu = m_new_cu.get ();
     }
@@ -7116,16 +7095,10 @@  cutu_reader::keep ()
   gdb_assert (!dummy_p);
   if (m_new_cu != NULL)
     {
-      /* We know that m_this_cu->cu is set, since we are in the process of
-         parsing the CU.  */
-      gdb_assert (m_this_cu->cu != nullptr);
-      dwarf2_per_objfile *dwarf2_per_objfile = m_this_cu->cu->per_objfile;
-
-      /* Link this CU into read_in_chain.  */
-      m_this_cu->cu->read_in_chain = dwarf2_per_objfile->per_bfd->read_in_chain;
-      dwarf2_per_objfile->per_bfd->read_in_chain = m_this_cu;
-      /* The chain owns it now.  */
-      m_new_cu.release ();
+      /* Save this dwarf2_cu in the per_objfile.  The per_objfile owns it
+         now.  */
+      dwarf2_per_objfile *per_objfile = m_new_cu->per_objfile;
+      per_objfile->set_cu (m_this_cu, m_new_cu.release ());
     }
 }
 
@@ -7163,7 +7136,7 @@  cutu_reader::cutu_reader (dwarf2_per_cu_data *this_cu,
 			this_cu->is_debug_types ? "type" : "comp",
 			sect_offset_str (this_cu->sect_off));
 
-  gdb_assert (this_cu->cu == NULL);
+  gdb_assert (dwarf2_per_objfile->get_cu (this_cu) == nullptr);
 
   abbrev_section = (dwo_file != NULL
 		    ? &dwo_file->sections.abbrev
@@ -7525,8 +7498,7 @@  process_psymtab_comp_unit (dwarf2_per_cu_data *this_cu,
      necessary because we skipped some symbols when we first
      read in the compilation unit (see load_partial_dies).
      This problem could be avoided, but the benefit is unclear.  */
-  if (this_cu->cu != NULL)
-    free_one_cached_comp_unit (this_cu, per_objfile);
+  per_objfile->remove_cu (this_cu);
 
   cutu_reader reader (this_cu, per_objfile, nullptr, nullptr, false);
 
@@ -7555,10 +7527,10 @@  process_psymtab_comp_unit (dwarf2_per_cu_data *this_cu,
 				      reader.comp_unit_die,
 				      pretend_language);
 
-  this_cu->lang = this_cu->cu->language;
+  this_cu->lang = reader.cu->language;
 
   /* Age out any secondary CUs.  */
-  age_cached_comp_units (per_objfile);
+  per_objfile->age_comp_units ();
 }
 
 /* Reader function for build_type_psymtabs.  */
@@ -8913,7 +8885,9 @@  maybe_queue_comp_unit (struct dwarf2_cu *dependent_cu,
      not queue PER_CU, just tell our caller to load its DIEs.  */
   if (per_cu->per_bfd->reading_partial_symbols)
     {
-      if (per_cu->cu == NULL || per_cu->cu->dies == NULL)
+      dwarf2_cu *cu = per_objfile->get_cu (per_cu);
+
+      if (cu == NULL || cu->dies == NULL)
 	return 1;
       return 0;
     }
@@ -8929,9 +8903,10 @@  maybe_queue_comp_unit (struct dwarf2_cu *dependent_cu,
 
   /* If the compilation unit is already loaded, just mark it as
      used.  */
-  if (per_cu->cu != NULL)
+  dwarf2_cu *cu = per_objfile->get_cu (per_cu);
+  if (cu != nullptr)
     {
-      per_cu->cu->last_used = 0;
+      cu->last_used = 0;
       return 0;
     }
 
@@ -8958,47 +8933,51 @@  process_queue (struct dwarf2_per_objfile *dwarf2_per_objfile)
   while (!dwarf2_per_objfile->per_bfd->queue.empty ())
     {
       dwarf2_queue_item &item = dwarf2_per_objfile->per_bfd->queue.front ();
+      dwarf2_per_cu_data *per_cu = item.per_cu;
 
-      if (!dwarf2_per_objfile->symtab_set_p (item.per_cu)
-	  /* Skip dummy CUs.  */
-	  && item.per_cu->cu != NULL)
+      if (!dwarf2_per_objfile->symtab_set_p (per_cu))
 	{
-	  struct dwarf2_per_cu_data *per_cu = item.per_cu;
-	  unsigned int debug_print_threshold;
-	  char buf[100];
+	  dwarf2_cu *cu = dwarf2_per_objfile->get_cu (per_cu);
 
-	  if (per_cu->is_debug_types)
-	    {
-	      struct signatured_type *sig_type =
-		(struct signatured_type *) per_cu;
-
-	      sprintf (buf, "TU %s at offset %s",
-		       hex_string (sig_type->signature),
-		       sect_offset_str (per_cu->sect_off));
-	      /* There can be 100s of TUs.
-		 Only print them in verbose mode.  */
-	      debug_print_threshold = 2;
-	    }
-	  else
+	  /* Skip dummy CUs.  */
+	  if (cu != nullptr)
 	    {
-	      sprintf (buf, "CU at offset %s",
-		       sect_offset_str (per_cu->sect_off));
-	      debug_print_threshold = 1;
-	    }
+	      unsigned int debug_print_threshold;
+	      char buf[100];
+
+	      if (per_cu->is_debug_types)
+		{
+		  struct signatured_type *sig_type =
+		    (struct signatured_type *) per_cu;
+
+		  sprintf (buf, "TU %s at offset %s",
+			   hex_string (sig_type->signature),
+			   sect_offset_str (per_cu->sect_off));
+		  /* There can be 100s of TUs.
+		     Only print them in verbose mode.  */
+		  debug_print_threshold = 2;
+		}
+	      else
+		{
+		  sprintf (buf, "CU at offset %s",
+			   sect_offset_str (per_cu->sect_off));
+		  debug_print_threshold = 1;
+		}
 
-	  if (dwarf_read_debug >= debug_print_threshold)
-	    fprintf_unfiltered (gdb_stdlog, "Expanding symtab of %s\n", buf);
+	      if (dwarf_read_debug >= debug_print_threshold)
+		fprintf_unfiltered (gdb_stdlog, "Expanding symtab of %s\n", buf);
 
-	  if (per_cu->is_debug_types)
-	    process_full_type_unit (per_cu->cu, item.pretend_language);
-	  else
-	    process_full_comp_unit (per_cu->cu, item.pretend_language);
+	      if (per_cu->is_debug_types)
+		process_full_type_unit (cu, item.pretend_language);
+	      else
+		process_full_comp_unit (cu, item.pretend_language);
 
-	  if (dwarf_read_debug >= debug_print_threshold)
-	    fprintf_unfiltered (gdb_stdlog, "Done expanding %s\n", buf);
+	      if (dwarf_read_debug >= debug_print_threshold)
+		fprintf_unfiltered (gdb_stdlog, "Done expanding %s\n", buf);
+	    }
 	}
 
-      item.per_cu->queued = 0;
+      per_cu->queued = 0;
       dwarf2_per_objfile->per_bfd->queue.pop ();
     }
 
@@ -9074,7 +9053,8 @@  load_full_comp_unit (dwarf2_per_cu_data *this_cu,
 {
   gdb_assert (! this_cu->is_debug_types);
 
-  cutu_reader reader (this_cu, per_objfile, NULL, this_cu->cu, skip_partial);
+  dwarf2_cu *existing_cu = per_objfile->get_cu (this_cu);
+  cutu_reader reader (this_cu, per_objfile, NULL, existing_cu, skip_partial);
   if (reader.dummy_p)
     return;
 
@@ -18653,7 +18633,6 @@  find_partial_die (sect_offset sect_off, int offset_in_dwz, struct dwarf2_cu *cu)
 {
   struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
   struct objfile *objfile = dwarf2_per_objfile->objfile;
-  struct dwarf2_per_cu_data *per_cu = NULL;
   struct partial_die_info *pd = NULL;
 
   if (offset_in_dwz == cu->per_cu->is_dwz
@@ -18664,7 +18643,6 @@  find_partial_die (sect_offset sect_off, int offset_in_dwz, struct dwarf2_cu *cu)
 	return { cu, pd };
       /* We missed recording what we needed.
 	 Load all dies and try again.  */
-      per_cu = cu->per_cu;
     }
   else
     {
@@ -18676,22 +18654,26 @@  find_partial_die (sect_offset sect_off, int offset_in_dwz, struct dwarf2_cu *cu)
 		 sect_offset_str (cu->header.sect_off), sect_offset_str (sect_off),
 		 bfd_get_filename (objfile->obfd));
 	}
-      per_cu = dwarf2_find_containing_comp_unit (sect_off, offset_in_dwz,
-						 dwarf2_per_objfile);
+      dwarf2_per_cu_data *per_cu
+	= dwarf2_find_containing_comp_unit (sect_off, offset_in_dwz,
+					    dwarf2_per_objfile);
 
-      if (per_cu->cu == NULL || per_cu->cu->partial_dies == NULL)
-	load_partial_comp_unit (per_cu, cu->per_objfile, nullptr);
+      cu = dwarf2_per_objfile->get_cu (per_cu);
+      if (cu == NULL || cu->partial_dies == NULL)
+	load_partial_comp_unit (per_cu, dwarf2_per_objfile, nullptr);
 
-      per_cu->cu->last_used = 0;
-      pd = per_cu->cu->find_partial_die (sect_off);
+      cu = dwarf2_per_objfile->get_cu (per_cu);
+
+      cu->last_used = 0;
+      pd = cu->find_partial_die (sect_off);
     }
 
   /* If we didn't find it, and not all dies have been loaded,
      load them all and try again.  */
 
-  if (pd == NULL && per_cu->load_all_dies == 0)
+  if (pd == NULL && cu->per_cu->load_all_dies == 0)
     {
-      per_cu->load_all_dies = 1;
+      cu->per_cu->load_all_dies = 1;
 
       /* This is nasty.  When we reread the DIEs, somewhere up the call chain
 	 THIS_CU->cu may already be in use.  So we can't just free it and
@@ -18699,9 +18681,9 @@  find_partial_die (sect_offset sect_off, int offset_in_dwz, struct dwarf2_cu *cu)
 	 DIEs alone (which can still be in use, e.g. in scan_partial_symbols),
 	 and clobber THIS_CU->cu->partial_dies with the hash table for the new
 	 set.  */
-      load_partial_comp_unit (per_cu, cu->per_objfile, cu);
+      load_partial_comp_unit (cu->per_cu, dwarf2_per_objfile, cu);
 
-      pd = per_cu->cu->find_partial_die (sect_off);
+      pd = cu->find_partial_die (sect_off);
     }
 
   if (pd == NULL)
@@ -18709,7 +18691,7 @@  find_partial_die (sect_offset sect_off, int offset_in_dwz, struct dwarf2_cu *cu)
 		    _("could not find partial DIE %s "
 		      "in cache [from module %s]\n"),
 		    sect_offset_str (sect_off), bfd_get_filename (objfile->obfd));
-  return { per_cu->cu, pd };
+  return { cu, pd };
 }
 
 /* See if we can figure out if the class lives in a namespace.  We do
@@ -19389,7 +19371,7 @@  dwarf2_read_addr_index (dwarf2_per_cu_data *per_cu,
 			dwarf2_per_objfile *dwarf2_per_objfile,
 			unsigned int addr_index)
 {
-  struct dwarf2_cu *cu = per_cu->cu;
+  struct dwarf2_cu *cu = dwarf2_per_objfile->get_cu (per_cu);
   gdb::optional<ULONGEST> addr_base;
   int addr_size;
 
@@ -22230,7 +22212,7 @@  follow_die_offset (sect_offset sect_off, int offset_in_dwz,
       if (maybe_queue_comp_unit (cu, per_cu, dwarf2_per_objfile, cu->language))
 	load_full_comp_unit (per_cu, dwarf2_per_objfile, false, cu->language);
 
-      target_cu = per_cu->cu;
+      target_cu = dwarf2_per_objfile->get_cu (per_cu);
     }
   else if (cu->dies == NULL)
     {
@@ -22290,7 +22272,7 @@  dwarf2_fetch_die_loc_sect_off (sect_offset sect_off,
   struct dwarf2_locexpr_baton retval;
   struct objfile *objfile = dwarf2_per_objfile->objfile;
 
-  dwarf2_cu *cu = per_cu->cu;
+  dwarf2_cu *cu = dwarf2_per_objfile->get_cu (per_cu);
   if (cu == nullptr)
     cu = load_cu (per_cu, dwarf2_per_objfile, false);
 
@@ -22375,7 +22357,7 @@  dwarf2_fetch_die_loc_sect_off (sect_offset sect_off,
   retval.per_objfile = dwarf2_per_objfile;
   retval.per_cu = cu->per_cu;
 
-  age_cached_comp_units (dwarf2_per_objfile);
+  dwarf2_per_objfile->age_comp_units ();
 
   return retval;
 }
@@ -22431,7 +22413,7 @@  dwarf2_fetch_constant_bytes (sect_offset sect_off,
   enum bfd_endian byte_order;
   struct objfile *objfile = per_objfile->objfile;
 
-  dwarf2_cu *cu = per_cu->cu;
+  dwarf2_cu *cu = per_objfile->get_cu (per_cu);
   if (cu == nullptr)
     cu = load_cu (per_cu, per_objfile, false);
 
@@ -22554,7 +22536,7 @@  dwarf2_fetch_die_type_sect_off (sect_offset sect_off,
 {
   struct die_info *die;
 
-  dwarf2_cu *cu = per_cu->cu;
+  dwarf2_cu *cu = per_objfile->get_cu (per_cu);
   if (cu == nullptr)
     cu = load_cu (per_cu, per_objfile, false);
 
@@ -22604,7 +22586,7 @@  follow_die_sig_1 (struct die_info *src_die, struct signatured_type *sig_type,
 			     language_minimal))
     read_signatured_type (sig_type, dwarf2_per_objfile);
 
-  sig_cu = sig_type->per_cu.cu;
+  sig_cu = dwarf2_per_objfile->get_cu (&sig_type->per_cu);
   gdb_assert (sig_cu != NULL);
   gdb_assert (to_underlying (sig_type->type_offset_in_section) != 0);
   temp_die.sect_off = sig_type->type_offset_in_section;
@@ -22778,11 +22760,11 @@  load_full_type_unit (dwarf2_per_cu_data *per_cu,
   gdb_assert (per_cu->is_debug_types);
   sig_type = (struct signatured_type *) per_cu;
 
-  gdb_assert (per_cu->cu == NULL);
+  gdb_assert (per_objfile->get_cu (per_cu) == nullptr);
 
   read_signatured_type (sig_type, per_objfile);
 
-  gdb_assert (per_cu->cu != NULL);
+  gdb_assert (per_objfile->get_cu (per_cu) != nullptr);
 }
 
 /* Read in a signatured type and build its CU and DIEs.
@@ -22796,7 +22778,7 @@  read_signatured_type (signatured_type *sig_type,
   struct dwarf2_per_cu_data *per_cu = &sig_type->per_cu;
 
   gdb_assert (per_cu->is_debug_types);
-  gdb_assert (per_cu->cu == NULL);
+  gdb_assert (per_objfile->get_cu (per_cu) == nullptr);
 
   cutu_reader reader (per_cu, per_objfile, nullptr, nullptr, false);
 
@@ -23503,14 +23485,6 @@  dwarf2_cu::dwarf2_cu (dwarf2_per_cu_data *per_cu,
     producer_is_codewarrior (false),
     processing_has_namespace_info (false)
 {
-  per_cu->cu = this;
-}
-
-/* Destroy a dwarf2_cu.  */
-
-dwarf2_cu::~dwarf2_cu ()
-{
-  per_cu->cu = NULL;
 }
 
 /* Initialize basic fields of dwarf_cu CU according to DIE COMP_UNIT_DIE.  */
@@ -23534,72 +23508,80 @@  prepare_one_comp_unit (struct dwarf2_cu *cu, struct die_info *comp_unit_die,
   cu->producer = dwarf2_string_attr (comp_unit_die, DW_AT_producer, cu);
 }
 
-/* Increase the age counter on each cached compilation unit, and free
-   any that are too old.  */
+/* See read.h.  */
 
-static void
-age_cached_comp_units (struct dwarf2_per_objfile *dwarf2_per_objfile)
+dwarf2_cu *
+dwarf2_per_objfile::get_cu (dwarf2_per_cu_data *per_cu)
 {
-  struct dwarf2_per_cu_data *per_cu, **last_chain;
+  auto it = m_dwarf2_cus.find (per_cu);
+  if (it == m_dwarf2_cus.end ())
+    return nullptr;
 
-  dwarf2_clear_marks (dwarf2_per_objfile->per_bfd->read_in_chain);
-  per_cu = dwarf2_per_objfile->per_bfd->read_in_chain;
-  while (per_cu != NULL)
+  return it->second;
+}
+
+/* See read.h.  */
+
+void
+dwarf2_per_objfile::set_cu (dwarf2_per_cu_data *per_cu, dwarf2_cu *cu)
+{
+  gdb_assert (this->get_cu (per_cu) == nullptr);
+
+  m_dwarf2_cus[per_cu] = cu;
+}
+
+/* See read.h.  */
+
+void
+dwarf2_per_objfile::age_comp_units ()
+{
+  /* Start by clearing all marks.  */
+  for (auto pair : m_dwarf2_cus)
+    pair.second->mark = false;
+
+  /* Traverse all CUs, mark them and their dependencies if used recently
+     enough.  */
+  for (auto pair : m_dwarf2_cus)
     {
-      per_cu->cu->last_used ++;
-      if (per_cu->cu->last_used <= dwarf_max_cache_age)
-	dwarf2_mark (per_cu->cu);
-      per_cu = per_cu->cu->read_in_chain;
+      dwarf2_cu *cu = pair.second;
+
+      cu->last_used++;
+      if (cu->last_used <= dwarf_max_cache_age)
+	dwarf2_mark (cu);
     }
 
-  per_cu = dwarf2_per_objfile->per_bfd->read_in_chain;
-  last_chain = &dwarf2_per_objfile->per_bfd->read_in_chain;
-  while (per_cu != NULL)
+  /* Delete all CUs still not marked.  */
+  for (auto it = m_dwarf2_cus.begin (); it != m_dwarf2_cus.end ();)
     {
-      struct dwarf2_per_cu_data *next_cu;
+      dwarf2_cu *cu = it->second;
 
-      next_cu = per_cu->cu->read_in_chain;
-
-      if (!per_cu->cu->mark)
+      if (!cu->mark)
 	{
-	  delete per_cu->cu;
-	  *last_chain = next_cu;
+	  delete cu;
+	  it = m_dwarf2_cus.erase (it);
 	}
       else
-	last_chain = &per_cu->cu->read_in_chain;
-
-      per_cu = next_cu;
+	it++;
     }
 }
 
-/* Remove a single compilation unit from the cache.  */
+/* See read.h.  */
 
-static void
-free_one_cached_comp_unit (dwarf2_per_cu_data *target_per_cu,
-			   dwarf2_per_objfile *dwarf2_per_objfile)
+void
+dwarf2_per_objfile::remove_cu (dwarf2_per_cu_data *per_cu)
 {
-  struct dwarf2_per_cu_data *per_cu, **last_chain;
-
-  per_cu = dwarf2_per_objfile->per_bfd->read_in_chain;
-  last_chain = &dwarf2_per_objfile->per_bfd->read_in_chain;
-  while (per_cu != NULL)
-    {
-      struct dwarf2_per_cu_data *next_cu;
+  auto it = m_dwarf2_cus.find (per_cu);
+  if (it == m_dwarf2_cus.end ())
+    return;
 
-      next_cu = per_cu->cu->read_in_chain;
+  delete it->second;
 
-      if (per_cu == target_per_cu)
-	{
-	  delete per_cu->cu;
-	  per_cu->cu = NULL;
-	  *last_chain = next_cu;
-	  break;
-	}
-      else
-	last_chain = &per_cu->cu->read_in_chain;
+  m_dwarf2_cus.erase (it);
+}
 
-      per_cu = next_cu;
-    }
+dwarf2_per_objfile::~dwarf2_per_objfile ()
+{
+  remove_all_cus ();
 }
 
 /* A set of CU "per_cu" pointer, DIE offset, and GDB type pointer.
@@ -23801,27 +23783,30 @@  dwarf2_add_dependence (struct dwarf2_cu *cu,
 
 /* Subroutine of dwarf2_mark to pass to htab_traverse.
    Set the mark field in every compilation unit in the
-   cache that we must keep because we are keeping CU.  */
+   cache that we must keep because we are keeping CU.
+
+   DATA is the dwarf2_per_objfile object in which to look up CUs.  */
 
 static int
 dwarf2_mark_helper (void **slot, void *data)
 {
-  struct dwarf2_per_cu_data *per_cu;
-
-  per_cu = (struct dwarf2_per_cu_data *) *slot;
+  dwarf2_per_cu_data *per_cu = (dwarf2_per_cu_data *) *slot;
+  dwarf2_per_objfile *per_objfile = (dwarf2_per_objfile *) data;
+  dwarf2_cu *cu = per_objfile->get_cu (per_cu);
 
   /* cu->dependencies references may not yet have been ever read if QUIT aborts
      reading of the chain.  As such dependencies remain valid it is not much
      useful to track and undo them during QUIT cleanups.  */
-  if (per_cu->cu == NULL)
+  if (cu == nullptr)
     return 1;
 
-  if (per_cu->cu->mark)
+  if (cu->mark)
     return 1;
-  per_cu->cu->mark = true;
 
-  if (per_cu->cu->dependencies != NULL)
-    htab_traverse (per_cu->cu->dependencies, dwarf2_mark_helper, NULL);
+  cu->mark = true;
+
+  if (cu->dependencies != nullptr)
+    htab_traverse (cu->dependencies, dwarf2_mark_helper, per_objfile);
 
   return 1;
 }
@@ -23834,19 +23819,11 @@  dwarf2_mark (struct dwarf2_cu *cu)
 {
   if (cu->mark)
     return;
+
   cu->mark = true;
-  if (cu->dependencies != NULL)
-    htab_traverse (cu->dependencies, dwarf2_mark_helper, NULL);
-}
 
-static void
-dwarf2_clear_marks (struct dwarf2_per_cu_data *per_cu)
-{
-  while (per_cu)
-    {
-      per_cu->cu->mark = false;
-      per_cu = per_cu->cu->read_in_chain;
-    }
+  if (cu->dependencies != nullptr)
+    htab_traverse (cu->dependencies, dwarf2_mark_helper, cu->per_objfile);
 }
 
 /* Trivial hash function for partial_die_info: the hash value of a DIE
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index b75be312217..996cf55af22 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -43,6 +43,7 @@  struct tu_stats
   int nr_all_type_units_reallocs;
 };
 
+struct dwarf2_cu;
 struct dwarf2_debug_sections;
 struct dwarf2_per_cu_data;
 struct mapped_index;
@@ -115,9 +116,6 @@  struct dwarf2_per_bfd
      TU.  */
   signatured_type *get_tu (int index);
 
-  /* Free all cached compilation units.  */
-  void free_cached_comp_units ();
-
   /* A convenience function to allocate a dwarf2_per_cu_data.  The
      returned object has its "index" field set properly.  The object
      is allocated on the dwarf2_per_bfd obstack.  */
@@ -189,10 +187,6 @@  struct dwarf2_per_bfd
      are doing.  */
   struct tu_stats tu_stats {};
 
-  /* A chain of compilation units that are currently read in, so that
-     they can be freed later.  */
-  dwarf2_per_cu_data *read_in_chain = NULL;
-
   /* A table mapping DW_AT_dwo_name values to struct dwo_file objects.
      This is NULL if the table hasn't been allocated yet.  */
   htab_up dwo_files;
@@ -303,6 +297,8 @@  struct dwarf2_per_objfile
     : objfile (objfile), per_bfd (per_bfd)
   {}
 
+  ~dwarf2_per_objfile ();
+
   /* Return pointer to string at .debug_line_str offset as read from BUF.
      BUF is assumed to be in a compilation unit described by CU_HEADER.
      Return *BYTES_READ_PTR count of bytes read from BUF.  */
@@ -344,6 +340,22 @@  struct dwarf2_per_objfile
      UNSIGNED_P controls if the integer is unsigned or not.  */
   struct type *int_type (int size_in_bytes, bool unsigned_p) const;
 
+  /* Get the dwarf2_cu matching PER_CU for this objfile.  */
+  dwarf2_cu *get_cu (dwarf2_per_cu_data *per_cu);
+
+  /* Set the dwarf2_cu matching PER_CU for this objfile.  */
+  void set_cu (dwarf2_per_cu_data *per_cu, dwarf2_cu *cu);
+
+  /* Remove/free the dwarf2_cu matching PER_CU for this objfile.  */
+  void remove_cu (dwarf2_per_cu_data *per_cu);
+
+  /* Free all cached compilation units.  */
+  void remove_all_cus ();
+
+  /* Increase the age counter on each CU compilation unit and free
+     any that are too old.  */
+  void age_comp_units ();
+
   /* Back link.  */
   struct objfile *objfile;
 
@@ -372,6 +384,10 @@  struct dwarf2_per_objfile
 
   /* Map from signatured types to the corresponding struct type.  */
   std::unordered_map<signatured_type *, struct type *> m_type_map;
+
+  /* Map from the objfile-independent dwarf2_per_cu_data instances to the
+     corresponding objfile-dependent dwarf2_cu instances.  */
+  std::unordered_map<dwarf2_per_cu_data *, dwarf2_cu *> m_dwarf2_cus;
 };
 
 /* Get the dwarf2_per_objfile associated to OBJFILE.  */
@@ -455,11 +471,6 @@  struct dwarf2_per_cu_data
      not the DWO file.  */
   struct dwarf2_section_info *section;
 
-  /* Set to non-NULL iff this CU is currently loaded.  When it gets freed out
-     of the CU cache it gets reset to NULL again.  This is left as NULL for
-     dummy CUs (a CU header, but nothing else).  */
-  struct dwarf2_cu *cu;
-
   /* The unit type of this CU.  */
   enum dwarf_unit_type unit_type;