gdb/dwarf: make abbrev_table_cache read-through, make it mandatory

Message ID 20260106060051.1782630-1-simon.marchi@polymtl.ca
State New
Headers
Series gdb/dwarf: make abbrev_table_cache read-through, make it mandatory |

Commit Message

Simon Marchi Jan. 6, 2026, 6 a.m. UTC
  From: Simon Marchi <simon.marchi@efficios.com>

This patch implements some performance improvements initially sent as
part of this series [1], but now as a single patch.  There was some push
back from Tom regarding the increased memory usage, but I think that my
solution is still desirable: it makes the code simpler, and I think that
the memory usage it not an issue (the usage is transient and the amount
of memory used by the abbrev tables is relatively low).

As a reminder, here is the problem I'm looking to solve: when using
split DWARF (.dwo files) plus type units, all units inside a .dwo file
(generally one compile unit plus many type units) share the same abbrev
table.  So we end up re-reading that same abbrev tables many times, and
its gets very noticeable in function process_skeletonless_type_units.

cooked_index_worker_debug_info::process_type_units does some work to
avoid this problem, but it only works when everything is in the main
file (not using split DWARF).

Right now, we cache abbrev tables only in specific cases during
indexing, like when one CU imports things from another CU.  My previous
series changed cutu_reader so that it would add any abbrev table it
would read to the abbrev table cache (if it was passed one as a
parameter).  This allowed using a cache in
process_skeletonless_type_units and cut the time down significantly.

This patch goes a bit further in order to simplify cutu_reader even
further:

 - It makes the abbrev table cache read-through, meaning that when you
   request an abbrev table and it's not in the cache, it will go read
   it (and cache it).

 - It makes passing an abbrev table cache to the constructors mandatory
   (even when we wouldn't benefit from using a cache).

The result is that cutu_reader doesn't need to manage multiple cases of
how to obtain an abbrev table, and it doesn't need to manage adding
abbrev tables to the cache.  It no longer needs to manage the ownership
of the abbrev tables either: the abbrev tables are always owned by the
cache.  And the cases of abbrev table sharing are well handled
transparently.

This means that we pay a small price when we don't necessarily need to
(sometimes building and destroying an abbrev_table_cache for just one
cutu_reader), but I think that this price is not significant and the
code simplification is welcome.

In concrete terms, this patch:

 - changes abbrev_table_cache::find to become abbrev_table_cache::get,
   making it read-through
 - removes abbrev_table_cache::add
 - removes the abbrev_table parameter from the main cutu_reader
   constructor
 - makes the abbrev_table_cache parameter mandatory (a reference) in the
   main cutu_reader constructor, adds it to the alternative constructor,
   and passes it down to a few methods
 - adjusts the cutu_reader code obtaining an abbrev table to just call
   abbrev_table_cache::get
 - adjusts all the cutu_reader users to pass an abbrev_table_cache (if
   not already)
 - removes the code in
   cooked_index_worker_debug_info::process_type_units meant to
   efficiently handle TUs that share abbrev tables - the cache now does
   this

The specific split DWARF + type units performance problem at the origin
of this work gets fixed by the fact that
cooked_index_worker_debug_info::process_skeletonless_type_unit now
passes an abbrev table cache to cutu_reader, and
cutu_reader::read_cutu_die_from_dwo uses it.

As a test, I'm using a build of Blender compiled with -gsplit-dwarf and
-fdebug-types-section.  I run this:

    $ ./gdb -nx -q --data-directory=data-directory -ex 'maint set dwarf sync on' -ex 'maintenance set per-command time on' -ex "file /data1/smarchi/blender-build/relwithdebinfo-clang-debugtypes-splitdwarf/bin/blender" -batch

and look at the time taken by the "DWARF skeletonless type units"
step.  Before looks like:

    Time for "DWARF skeletonless type units": wall 11.131, user 10.699, sys 0.431, user+sys 11.130, 100.0 % CPU

and after looks like:

    Time for "DWARF skeletonless type units": wall 1.751, user 1.221, sys 0.518, user+sys 1.739, 99.3 % CPU

The total run time (wall clock time) of the command goes from about 18.5
seconds to about 9.5 seconds.

I removed this assert in cutu_reader, because it relies on abbrev_cache
as a flag for whether we're in the indexer:

      /* If an existing_cu is provided, a dwarf2_cu must not exist for
	 this_cu in per_objfile yet.  Here, CACHE doubles as a flag to
	 let us know that the CU is being scanned using the parallel
	 indexer.  This assert is avoided in this case because (1) it
	 is irrelevant, and (2) the get_cu method is not
	 thread-safe.  */
      gdb_assert (abbrev_cache != nullptr
		  || per_objfile.get_cu (&this_cu) == nullptr);

It's not clear to me if this assert is important or how to implement it
differently.

[1] https://inbox.sourceware.org/gdb-patches/20250326200002.136200-4-simon.marchi@efficios.com/

Change-Id: Idf8a514326fb35be8bda0d7ebe3cee4b7304941a
---
 gdb/Makefile.in                  |   1 -
 gdb/dwarf2/abbrev-table-cache.c  |  33 -----
 gdb/dwarf2/abbrev-table-cache.h  |  19 +--
 gdb/dwarf2/abbrev.c              |   5 +-
 gdb/dwarf2/abbrev.h              |   5 +-
 gdb/dwarf2/cooked-index-worker.c |   2 -
 gdb/dwarf2/cooked-index-worker.h |   2 +-
 gdb/dwarf2/cooked-indexer.c      |   7 +-
 gdb/dwarf2/read.c                | 217 +++++++++----------------------
 gdb/dwarf2/read.h                |  28 ++--
 10 files changed, 81 insertions(+), 238 deletions(-)
 delete mode 100644 gdb/dwarf2/abbrev-table-cache.c


base-commit: b46fee9c0bf0611cfa25de31707f7bf8b8bddcaf
  

Comments

Tom Tromey Jan. 13, 2026, 4:14 p.m. UTC | #1
>>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes:

Simon> As a test, I'm using a build of Blender compiled with -gsplit-dwarf and
Simon> -fdebug-types-section.  I run this:

How does it look on ordinary code?

Also what is the memory impact?

Simon> I removed this assert in cutu_reader, because it relies on abbrev_cache
Simon> as a flag for whether we're in the indexer:

Simon>       /* If an existing_cu is provided, a dwarf2_cu must not exist for
Simon> 	 this_cu in per_objfile yet.  Here, CACHE doubles as a flag to
Simon> 	 let us know that the CU is being scanned using the parallel
Simon> 	 indexer.  This assert is avoided in this case because (1) it
Simon> 	 is irrelevant, and (2) the get_cu method is not
Simon> 	 thread-safe.  */
Simon>       gdb_assert (abbrev_cache != nullptr
Simon> 		  || per_objfile.get_cu (&this_cu) == nullptr);

Simon> It's not clear to me if this assert is important or how to implement it
Simon> differently.

I would not worry about this one.

Simon>  abbrev_table_up
Simon>  abbrev_table::read (struct dwarf2_section_info *section,
Simon> -		    sect_offset sect_off)
Simon> +		    sect_offset sect_off, objfile *objfile)
Simon>  {
Simon>    bfd *abfd = section->get_bfd_owner ();
Simon>    const gdb_byte *abbrev_ptr;
Simon> @@ -81,8 +81,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
Simon>    abbrev_table_up abbrev_table (new struct abbrev_table (sect_off, section));
Simon>    struct obstack *obstack = &abbrev_table->m_abbrev_obstack;
 
Simon> -  /* Caller must ensure this.  */
Simon> -  gdb_assert (section->read_in);
Simon> +  section->read (objfile);

On the whole it's better, IMO, not to pass around the objfile.
Can the caller not ensure this in some particular case?

The reader as a whole moved to a model where sections are pre-read a
while back.

I see later:

@@ -2864,11 +2834,10 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit,
...
-  dwo_abbrev_section->read (objfile);

but I wonder if the DWO sections could be read when the file is opened.
Or maybe they already are, cutu_reader::locate_dwo_sections seems to
read them in... though there are so many DWO/DWP code paths I find it a
little hard to be sure.

Tom
  
Simon Marchi Jan. 13, 2026, 8:09 p.m. UTC | #2
On 1/13/26 11:14 AM, Tom Tromey wrote:
>>>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes:
> 
> Simon> As a test, I'm using a build of Blender compiled with -gsplit-dwarf and
> Simon> -fdebug-types-section.  I run this:
> 
> How does it look on ordinary code?
> 
> Also what is the memory impact?

I will prepare detailed benchmarks to answer these two questions.

> Simon> @@ -81,8 +81,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
> Simon>    abbrev_table_up abbrev_table (new struct abbrev_table (sect_off, section));
> Simon>    struct obstack *obstack = &abbrev_table->m_abbrev_obstack;
>  
> Simon> -  /* Caller must ensure this.  */
> Simon> -  gdb_assert (section->read_in);
> Simon> +  section->read (objfile);
> 
> On the whole it's better, IMO, not to pass around the objfile.
> Can the caller not ensure this in some particular case?
> 
> The reader as a whole moved to a model where sections are pre-read a
> while back.
> 
> I see later:
> 
> @@ -2864,11 +2834,10 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit,
> ...
> -  dwo_abbrev_section->read (objfile);
> 
> but I wonder if the DWO sections could be read when the file is opened.
> Or maybe they already are, cutu_reader::locate_dwo_sections seems to
> read them in... though there are so many DWO/DWP code paths I find it a
> little hard to be sure.

Yeah we can probably do that, I'll check.

Simon
  
Simon Marchi Jan. 14, 2026, 7:03 p.m. UTC | #3
On 2026-01-13 15:09, Simon Marchi wrote:
> On 1/13/26 11:14 AM, Tom Tromey wrote:
>>>>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes:
>>
>> Simon> As a test, I'm using a build of Blender compiled with -gsplit-dwarf and
>> Simon> -fdebug-types-section.  I run this:
>>
>> How does it look on ordinary code?
>>
>> Also what is the memory impact?
> 
> I will prepare detailed benchmarks to answer these two questions.

I did some benchmarks for memory usage and runtime.

I now use Blender as my large test program now, because it's much easier
to build than Telegram (and I think it results in even larger builds).
I built all configurations that are the cross-product of these
parameters:

 - CMake build type: Debug vs RelWithDebInfo
 - Compiler: gcc vs clang
 - Type units: yes vs no
 - Split DWARF: yes vs no

Here's the size of build directories, to give a sense of scale:

    20G     debug-gcc
    26G     debug-gcc-debugtypes
    18G     debug-gcc-debugtypes-splitdwarf
    16G     debug-gcc-splitdwarf

    11G     debug-clang
    12G     debug-clang-debugtypes
    8.6G    debug-clang-debugtypes-splitdwarf
    8.4G    debug-clang-splitdwarf

    20G     relwithdebinfo-gcc
    27G     relwithdebinfo-gcc-debugtypes
    16G     relwithdebinfo-gcc-debugtypes-splitdwarf
    14G     relwithdebinfo-gcc-splitdwarf

    8.3G    relwithdebinfo-clang
    9.1G    relwithdebinfo-clang-debugtypes
    5.3G    relwithdebinfo-clang-debugtypes-splitdwarf
    5.2G    relwithdebinfo-clang-splitdwarf

For memory usage, I used this patch:

https://review.lttng.org/c/binutils-gdb/+/14409

which sums up the obstack usage for all the abbrev tables at the end of
indexing.  It does not count all the data structures involved, but it
should be close.

For runtime, I do this and take the mean:

        for run in 1 2 3; do
          /usr/bin/time -f '%e' ./gdb -nx -q --data-directory=data-directory -ex 'maint set dwarf sync on' -ex "file $p" -batch
        done

Here are the results:

                                        |  Mem usage (MiB) |    Time (s)    |
    | Config         | Comp. | TU | DWO | Before |   After | Before | After |
    | -------------- | ----- | -- | --- | ------ | ------- | ------ | ----- |
    | Debug          | gcc   |    |     | 132.27 |  132.27 |  11.15 | 11.13 |
    | Debug          | gcc   |    | x   |  19.30 |  168.01 |  18.56 | 20.12 |
    | Debug          | gcc   | x  |     | 830.24 |  971.72 |  43.61 | 41.88 |
    | Debug          | gcc   | x  | x   |  19.30 |  377.24 | 141.59 | 74.87 |
    | -------------- | ----- | -- | --- | ------ | ------- | ------ | ----- |
    | Debug          | clang |    |     |  69.56 |   69.56 |   7.71 |  7.71 |
    | Debug          | clang |    | x   |  19.30 |   87.22 |   9.26 |  8.94 |
    | Debug          | clang | x  |     | 188.04 |  272.50 |  13.73 | 13.16 |
    | Debug          | clang | x  | x   |  19.30 |  142.04 |  21.70 | 12.58 |
    | -------------- | ----- | -- | --- | ------ | ------- | ------ | ----- |
    | RelWithDebInfo | gcc   |    |     | 136.05 |  136.05 |   8.49 |  8.79 |
    | RelWithDebInfo | gcc   |    | x   |  19.30 |  172.05 |  16.75 | 17.30 |
    | RelWithDebInfo | gcc   | x  |     | 827.30 | 1009.80 |  41.67 | 40.86 |
    | RelWithDebInfo | gcc   | x  | x   |  19.30 |  384.47 | 138.64 | 70.19 |
    | -------------- | ----- | -- | --- | ------ | ------- | ------ | ----- |
    | RelWithDebInfo | clang |    |     |  75.91 |   75.91 |   5.03 |  5.02 |
    | RelWithDebInfo | clang |    | x   |  19.30 |   92.48 |   6.49 |  6.25 |
    | RelWithDebInfo | clang | x  |     | 232.77 |  326.50 |  10.28 | 10.43 |
    | RelWithDebInfo | clang | x  | x   |  19.30 |  150.95 |  18.78 |  9.29 |


Regarding the memory, as indicated last time, we already do cache abbrev
tables for CUs in the standard case, following this fix:

https://gitlab.com/gnutools/binutils-gdb/-/commit/573d8bb08bfff4638405add40a6a61868af1f2a4

So there is no increase there.

For the DWO case, there is an expected increase, given that we now use
an abbrev cache where we didn't before.  The caching is likely not
useful in this particular case, because there is usually just one unit
per DWO file.

For the TU case, there is an expected increase, given that we now cache
the tables in process_type_units (replacing the manual optimization).
The caching is useful, but the manual optimization did essentially the
same job.

For the TU + DWO case, there is an expected increase, given that we now
use an abbrev cache where we didn't before.  And this is where the
caching is useful (the case I'm trying to fix).

I didn't reflect on why certain configurations take much more memory
than others.  Like why does enabling TUs makes it go from 100-ish MiB to
900-ish MiB?  CUs and TUs emitted to together share a single

Also note that this memory usage is transient, it is freed when the
cooked_index_worker object (and the results it contains) is freed.  We
could refactor things to that the abbrev table are freed a bit earlier
if we wanted to, because they are not useful after "done_reading",
AFAIK.

Regarding the time to index, there was a lot of variance in the times,
so I'm not sure how much the results can be trusted when the difference
is small.  I do not see a clear trend in all the case except TU + DWO.

Simon
  
Simon Marchi Jan. 15, 2026, 8:17 p.m. UTC | #4
On 1/13/26 3:09 PM, Simon Marchi wrote:
>> Simon> @@ -81,8 +81,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
>> Simon>    abbrev_table_up abbrev_table (new struct abbrev_table (sect_off, section));
>> Simon>    struct obstack *obstack = &abbrev_table->m_abbrev_obstack;
>>  
>> Simon> -  /* Caller must ensure this.  */
>> Simon> -  gdb_assert (section->read_in);
>> Simon> +  section->read (objfile);
>>
>> On the whole it's better, IMO, not to pass around the objfile.
>> Can the caller not ensure this in some particular case?
>>
>> The reader as a whole moved to a model where sections are pre-read a
>> while back.
>>
>> I see later:
>>
>> @@ -2864,11 +2834,10 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit,
>> ...
>> -  dwo_abbrev_section->read (objfile);
>>
>> but I wonder if the DWO sections could be read when the file is opened.
>> Or maybe they already are, cutu_reader::locate_dwo_sections seems to
>> read them in... though there are so many DWO/DWP code paths I find it a
>> little hard to be sure.
> 
> Yeah we can probably do that, I'll check.

Regarding this, I reverted to the previous behavior of relying on the
fact that the abbrev section is already read.  This requires the caller
making sure that it is true.  I think it is ok, because a caller that
wants to obtain an abbrev table does this:

      const abbrev_table &abbrev_table
	= abbrev_table_cache.get (abbrev_section, abbrev_sect_off);

It knows which section to obtain the abbrev table from, so it can make
sure the section is already read in.

There are 3 spots in my patch that call abbrev_table_cache::get:

 - cutu_reader::read_cutu_die_from_dwo: the source is a DWO file,
   sections are always pre-read by cutu_reader::locate_dwo_sections,
   or dwarf2_locate_dwp_sections, if the dwo file is from a dwp file
 - cutu_reader::cutu_reader, the alternative constructor only used when
   scanning compile units in a .dwo file for DWARF <= 4: same as the
   above, we know the section is always read
 - the main cutu_reader::cutu_reader constructor: when using the cooked
   indexer, it maps/reads the abbrev section early on.  But when using
   an index, the abbrev section won't be read in yet.  I added a

     abbrev_section->read (objfile);

   call just before the call to abbrev_table_cache::get there.  Before
   my patch, there is a call to abbrev_section->read, but only in the
   code path where we know we'll actually read an abbrev table.  With
   abbrev_table_cache::get, the caller doesn't know if the abbrev table
   is cached or not already, so the read call is unconditional.
   However, it is very cheap if the section is already read in, so I
   don't think it matters.

Does that look good?

Simon
  
Tom Tromey Jan. 22, 2026, 9:21 p.m. UTC | #5
>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:

Simon> Before
Simon>    my patch, there is a call to abbrev_section->read, but only in the
Simon>    code path where we know we'll actually read an abbrev table.  With
Simon>    abbrev_table_cache::get, the caller doesn't know if the abbrev table
Simon>    is cached or not already, so the read call is unconditional.
Simon>    However, it is very cheap if the section is already read in, so I
Simon>    don't think it matters.

Simon> Does that look good?

IIUC then yes I think so.  Thanks.

Tom
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9787cae26012..fa8d469968fc 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2003,7 +2003,6 @@  TAGFILES_WITH_SRCDIR = $(HFILES_WITH_SRCDIR)
 # Files that are used to support certain debuginfo formats
 DWARF2_SRCS = \
 	dwarf2/abbrev.c \
-	dwarf2/abbrev-table-cache.c \
 	dwarf2/ada-imported.c \
 	dwarf2/aranges.c \
 	dwarf2/attribute.c \
diff --git a/gdb/dwarf2/abbrev-table-cache.c b/gdb/dwarf2/abbrev-table-cache.c
deleted file mode 100644
index fe28012d94e5..000000000000
--- a/gdb/dwarf2/abbrev-table-cache.c
+++ /dev/null
@@ -1,33 +0,0 @@ 
-/* DWARF 2 abbrev table cache
-
-   Copyright (C) 2022-2026 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
-
-#include "dwarf2/abbrev-table-cache.h"
-
-void
-abbrev_table_cache::add (abbrev_table_up table)
-{
-  /* We allow this as a convenience to the caller.  */
-  if (table == nullptr)
-    return;
-
-  bool inserted = m_tables.emplace (std::move (table)).second;
-
-  /* If this one already existed, then it should have been reused.  */
-  gdb_assert (inserted);
-}
diff --git a/gdb/dwarf2/abbrev-table-cache.h b/gdb/dwarf2/abbrev-table-cache.h
index 30babbc33746..80712f6eef12 100644
--- a/gdb/dwarf2/abbrev-table-cache.h
+++ b/gdb/dwarf2/abbrev-table-cache.h
@@ -34,27 +34,20 @@  class abbrev_table_cache
   abbrev_table_cache &operator= (abbrev_table_cache &&) = default;
 
   /* Find an abbrev table coming from the abbrev section SECTION at
-     offset OFFSET.  Return the table, or nullptr if it has not yet
-     been registered.  */
-  const abbrev_table *find (dwarf2_section_info *section,
-			    sect_offset offset) const
+     offset OFFSET.  Read it in if necessary.  */
+  const abbrev_table &get (dwarf2_section_info *section,
+			   sect_offset offset, struct objfile *objfile)
   {
     abbrev_table_search_key key {section, offset};
 
     if (auto iter = m_tables.find (key);
 	iter != m_tables.end ())
-      return iter->get ();
+      return **iter;
 
-    return nullptr;
+    auto table = abbrev_table::read (section, offset, objfile);
+    return **m_tables.insert (std::move (table)).first;
   }
 
-  /* Add TABLE to this cache.  Ownership of TABLE is transferred to
-     the cache.  Note that a table at a given section+offset may only
-     be registered once -- a violation of this will cause an assert.
-     To avoid this, call the 'find' method first, to see if the table
-     has already been read.  */
-  void add (abbrev_table_up table);
-
 private:
   /* Key used to search for an existing abbrev table in M_TABLES.  */
   struct abbrev_table_search_key
diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c
index 49564db275b7..b06282f8b75c 100644
--- a/gdb/dwarf2/abbrev.c
+++ b/gdb/dwarf2/abbrev.c
@@ -72,7 +72,7 @@  tag_interesting_for_index (dwarf_tag tag)
 
 abbrev_table_up
 abbrev_table::read (struct dwarf2_section_info *section,
-		    sect_offset sect_off)
+		    sect_offset sect_off, objfile *objfile)
 {
   bfd *abfd = section->get_bfd_owner ();
   const gdb_byte *abbrev_ptr;
@@ -81,8 +81,7 @@  abbrev_table::read (struct dwarf2_section_info *section,
   abbrev_table_up abbrev_table (new struct abbrev_table (sect_off, section));
   struct obstack *obstack = &abbrev_table->m_abbrev_obstack;
 
-  /* Caller must ensure this.  */
-  gdb_assert (section->read_in);
+  section->read (objfile);
   abbrev_ptr = section->buffer + to_underlying (sect_off);
 
   while (true)
diff --git a/gdb/dwarf2/abbrev.h b/gdb/dwarf2/abbrev.h
index c5a3c6c39a62..3872338a0498 100644
--- a/gdb/dwarf2/abbrev.h
+++ b/gdb/dwarf2/abbrev.h
@@ -81,11 +81,10 @@  using abbrev_table_up = std::unique_ptr<abbrev_table>;
 struct abbrev_table
 {
   /* Read an abbrev table from the indicated section, at the given
-     offset.  The caller is responsible for ensuring that the section
-     has already been read.  */
+     offset.  */
 
   static abbrev_table_up read (struct dwarf2_section_info *section,
-			       sect_offset sect_off);
+			       sect_offset sect_off, struct objfile *objfile);
 
   /* Look up an abbrev in the table.
      Returns NULL if the abbrev is not found.  */
diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c
index 24be57edfb59..ef2427d0a71c 100644
--- a/gdb/dwarf2/cooked-index-worker.c
+++ b/gdb/dwarf2/cooked-index-worker.c
@@ -46,8 +46,6 @@  cooked_index_worker_result::get_reader (dwarf2_per_cu *per_cu)
 cutu_reader *
 cooked_index_worker_result::preserve (cutu_reader_up reader)
 {
-  m_abbrev_table_cache.add (reader->release_abbrev_table ());
-
   auto [it, inserted] = m_reader_hash.insert (std::move (reader));
   gdb_assert (inserted);
 
diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h
index 847b8ce5589f..85a9f348de93 100644
--- a/gdb/dwarf2/cooked-index-worker.h
+++ b/gdb/dwarf2/cooked-index-worker.h
@@ -50,7 +50,7 @@  class cooked_index_worker_result
     = default;
 
   /* Return the current abbrev table_cache.  */
-  const abbrev_table_cache &get_abbrev_table_cache () const
+  abbrev_table_cache &get_abbrev_table_cache ()
   { return m_abbrev_table_cache; }
 
   /* Return the DIE reader corresponding to PER_CU.  If no such reader
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index dbf83c695a11..6a95a72933b9 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -110,12 +110,11 @@  cooked_indexer::ensure_cu_exists (cutu_reader *reader,
   cutu_reader *result = m_index_storage->get_reader (per_cu);
   if (result == nullptr)
     {
-      const abbrev_table_cache &abbrev_table_cache
+      abbrev_table_cache &abbrev_table_cache
 	= m_index_storage->get_abbrev_table_cache ();
       auto new_reader
-	= std::make_unique<cutu_reader> (*per_cu, *per_objfile, nullptr,
-					 nullptr, false, language_minimal,
-					 &abbrev_table_cache);
+	= std::make_unique<cutu_reader> (*per_cu, *per_objfile, nullptr, false,
+					 language_minimal, abbrev_table_cache);
 
       if (new_reader->is_dummy ())
 	return nullptr;
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4b706f654e0d..22f6977bdcae 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -751,9 +751,6 @@  static unsigned int peek_abbrev_code (bfd *, const gdb_byte *);
 static unrelocated_addr read_addr_index (struct dwarf2_cu *cu,
 					 unsigned int addr_index);
 
-static sect_offset read_abbrev_offset (dwarf2_per_objfile *per_objfile,
-				       dwarf2_section_info *, sect_offset);
-
 static const char *read_indirect_string (dwarf2_per_objfile *per_objfile, bfd *,
 					 const gdb_byte *, const unit_head *,
 					 unsigned int *);
@@ -1895,9 +1892,10 @@  dw2_get_file_names (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile)
   if (this_cu->files_read)
     return this_cu->file_names;
 
-  cutu_reader reader (*this_cu, *per_objfile, nullptr,
+  abbrev_table_cache abbrev_table_cache;
+  cutu_reader reader (*this_cu, *per_objfile,
 		      per_objfile->get_cu (this_cu), true, language_minimal,
-		      nullptr);
+		      abbrev_table_cache);
   if (!reader.is_dummy ())
     dw2_get_file_names_reader (reader.cu (), reader.top_level_die ());
 
@@ -2482,35 +2480,6 @@  all_units_less_than (const dwarf2_per_cu &lhs, const section_and_offset &rhs)
   return lhs.sect_off () < rhs.offset;
 }
 
-/* Fetch the abbreviation table offset from a comp or type unit header.  */
-
-static sect_offset
-read_abbrev_offset (dwarf2_per_objfile *per_objfile,
-		    struct dwarf2_section_info *section,
-		    sect_offset sect_off)
-{
-  bfd *abfd = section->get_bfd_owner ();
-  const gdb_byte *info_ptr;
-  unsigned int initial_length_size, offset_size;
-  uint16_t version;
-
-  section->read (per_objfile->objfile);
-  info_ptr = section->buffer + to_underlying (sect_off);
-  read_initial_length (abfd, info_ptr, &initial_length_size);
-  offset_size = initial_length_size == 4 ? 4 : 8;
-  info_ptr += initial_length_size;
-
-  version = read_2_bytes (abfd, info_ptr);
-  info_ptr += 2;
-  if (version >= 5)
-    {
-      /* Skip unit type and address size.  */
-      info_ptr += 2;
-    }
-
-  return (sect_offset) read_offset (abfd, info_ptr, offset_size);
-}
-
 /* Add an entry for signature SIG to per_bfd->signatured_types.
 
    This functions leaves PER_BFD::ALL_UNITS unsorted.  The caller must call
@@ -2705,7 +2674,7 @@  lookup_signatured_type (struct dwarf2_cu *cu, ULONGEST sig)
 void
 cutu_reader::init_cu_die_reader (dwarf2_cu *cu, dwarf2_section_info *section,
 				 struct dwo_file *dwo_file,
-				 const struct abbrev_table *abbrev_table)
+				 const abbrev_table &abbrev_table)
 {
   gdb_assert (section->read_in && section->buffer != NULL);
   m_abfd = section->get_bfd_owner ();
@@ -2714,7 +2683,7 @@  cutu_reader::init_cu_die_reader (dwarf2_cu *cu, dwarf2_section_info *section,
   m_die_section = section;
   m_buffer = section->buffer;
   m_buffer_end = section->buffer + section->size;
-  m_abbrev_table = abbrev_table;
+  m_abbrev_table = &abbrev_table;
 }
 
 /* Subroutine of cutu_reader to simplify it.
@@ -2740,7 +2709,8 @@  cutu_reader::init_cu_die_reader (dwarf2_cu *cu, dwarf2_section_info *section,
 void
 cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit,
 				     die_info *stub_comp_unit_die,
-				     const char *stub_comp_dir)
+				     const char *stub_comp_dir,
+				     abbrev_table_cache &abbrev_table_cache)
 {
   dwarf2_per_cu *per_cu = cu->per_cu;
   struct objfile *objfile = cu->per_objfile->objfile;
@@ -2864,11 +2834,10 @@  cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit,
 			     ? cu->header.addr_size
 			     : cu->header.offset_size);
 
-  dwo_abbrev_section->read (objfile);
-  m_dwo_abbrev_table
-    = abbrev_table::read (dwo_abbrev_section, cu->header.abbrev_sect_off);
-  this->init_cu_die_reader (cu, section, dwo_unit->dwo_file,
-			    m_dwo_abbrev_table.get ());
+  const abbrev_table &abbrev_table
+    = abbrev_table_cache.get (dwo_abbrev_section, cu->header.abbrev_sect_off,
+			      objfile);
+  this->init_cu_die_reader (cu, section, dwo_unit->dwo_file, abbrev_table);
 
   /* Skip dummy compilation units.  */
   if (m_info_ptr >= begin_info_ptr + dwo_unit->length
@@ -2948,7 +2917,8 @@  void
 cutu_reader::init_tu_and_read_dwo_dies (dwarf2_per_cu *this_cu,
 					dwarf2_per_objfile *per_objfile,
 					dwarf2_cu *existing_cu,
-					enum language pretend_language)
+					enum language pretend_language,
+					abbrev_table_cache &abbrev_table_cache)
 {
   struct signatured_type *sig_type;
 
@@ -2981,7 +2951,8 @@  cutu_reader::init_tu_and_read_dwo_dies (dwarf2_per_cu *this_cu,
      could share abbrev tables.  */
 
   read_cutu_die_from_dwo (cu, sig_type->dwo_unit, NULL /* stub_comp_unit_die */,
-			  sig_type->dwo_unit->dwo_file->comp_dir);
+			  sig_type->dwo_unit->dwo_file->comp_dir,
+			  abbrev_table_cache);
   prepare_one_comp_unit (cu, pretend_language);
 }
 
@@ -2997,11 +2968,10 @@  cutu_reader::init_tu_and_read_dwo_dies (dwarf2_per_cu *this_cu,
 
 cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
 			  dwarf2_per_objfile &per_objfile,
-			  const struct abbrev_table *abbrev_table,
 			  dwarf2_cu *existing_cu,
 			  bool skip_partial,
 			  enum language pretend_language,
-			  const abbrev_table_cache *abbrev_cache)
+			  abbrev_table_cache &abbrev_table_cache)
 {
   struct objfile *objfile = per_objfile.objfile;
   struct dwarf2_section_info *section = this_cu.section ();
@@ -3025,9 +2995,8 @@  cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
     {
       /* Narrow down the scope of possibilities to have to understand.  */
       gdb_assert (this_cu.is_debug_types ());
-      gdb_assert (abbrev_table == NULL);
       init_tu_and_read_dwo_dies (&this_cu, &per_objfile, existing_cu,
-				 pretend_language);
+				 pretend_language, abbrev_table_cache);
       return;
     }
 
@@ -3055,14 +3024,6 @@  cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
     }
   else
     {
-      /* If an existing_cu is provided, a dwarf2_cu must not exist for
-	 this_cu in per_objfile yet.  Here, CACHE doubles as a flag to
-	 let us know that the CU is being scanned using the parallel
-	 indexer.  This assert is avoided in this case because (1) it
-	 is irrelevant, and (2) the get_cu method is not
-	 thread-safe.  */
-      gdb_assert (abbrev_cache != nullptr
-		  || per_objfile.get_cu (&this_cu) == nullptr);
       m_new_cu = std::make_unique<dwarf2_cu> (&this_cu, &per_objfile);
       cu = m_new_cu.get ();
     }
@@ -3124,25 +3085,9 @@  cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
     m_dummy_p = true;
   else
     {
-      /* If we don't have them yet, read the abbrevs for this
-	 compilation unit.  And if we need to read them now, make sure
-	 they're freed when we're done.  */
-      if (abbrev_table != NULL)
-	gdb_assert (cu->header.abbrev_sect_off == abbrev_table->sect_off);
-      else
-	{
-	  if (abbrev_cache != nullptr)
-	    abbrev_table = abbrev_cache->find (abbrev_section,
-					       cu->header.abbrev_sect_off);
-	  if (abbrev_table == nullptr)
-	    {
-	      abbrev_section->read (objfile);
-	      m_abbrev_table_holder
-		= abbrev_table::read (abbrev_section,
-				      cu->header.abbrev_sect_off);
-	      abbrev_table = m_abbrev_table_holder.get ();
-	    }
-	}
+      const abbrev_table &abbrev_table
+	= abbrev_table_cache.get (abbrev_section, cu->header.abbrev_sect_off,
+				  objfile);
 
       /* Read the top level CU/TU die.  */
       this->init_cu_die_reader (cu, section, NULL, abbrev_table);
@@ -3177,7 +3122,8 @@  cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
 
 	      dwo_unit = lookup_dwo_unit (cu, m_top_level_die, dwo_name);
 	      if (dwo_unit != NULL)
-		read_cutu_die_from_dwo (cu, dwo_unit, m_top_level_die, nullptr);
+		read_cutu_die_from_dwo (cu, dwo_unit, m_top_level_die, nullptr,
+					abbrev_table_cache);
 	      else
 		{
 		  /* Yikes, we couldn't find the rest of the DIE, we only have
@@ -3221,7 +3167,8 @@  cutu_reader::release_cu ()
 cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
 			  dwarf2_per_objfile &per_objfile,
 			  language pretend_language, dwarf2_cu &parent_cu,
-			  dwo_file &dwo_file)
+			  dwo_file &dwo_file,
+			  abbrev_table_cache &abbrev_table_cache)
 {
   struct objfile *objfile = per_objfile.objfile;
   struct dwarf2_section_info *section = this_cu.section ();
@@ -3261,12 +3208,12 @@  cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
   else
     {
       abbrev_section->read (objfile);
-      m_abbrev_table_holder
-	= abbrev_table::read (abbrev_section,
-			      m_new_cu->header.abbrev_sect_off);
+      const abbrev_table &abbrev_table
+	= abbrev_table_cache.get (abbrev_section,
+				  m_new_cu->header.abbrev_sect_off, objfile);
 
       this->init_cu_die_reader (m_new_cu.get (), section, &dwo_file,
-				m_abbrev_table_holder.get ());
+				abbrev_table);
       m_top_level_die = this->read_toplevel_die ();
     }
 
@@ -3406,8 +3353,6 @@  class cooked_index_worker_debug_info : public cooked_index_worker
 
     dwarf_read_debug_printf ("Type unit statistics:");
     dwarf_read_debug_printf ("  %d TUs", per_bfd->num_type_units);
-    dwarf_read_debug_printf ("  %d uniq abbrev tables",
-			     tu_stats->nr_uniq_abbrev_tables);
     dwarf_read_debug_printf ("  %d symtabs from stmt_list entries",
 			     tu_stats->nr_symtabs);
     dwarf_read_debug_printf ("  %d symtab sharers",
@@ -3475,12 +3420,10 @@  cooked_index_worker_debug_info::process_unit
   cutu_reader *reader = storage->get_reader (this_cu);
   if (reader == nullptr)
     {
-      const abbrev_table_cache &abbrev_table_cache
-	= storage->get_abbrev_table_cache ();
-      auto new_reader = std::make_unique<cutu_reader> (*this_cu, *per_objfile,
-						       nullptr, nullptr, false,
-						       language_minimal,
-						       &abbrev_table_cache);
+      auto new_reader
+	= std::make_unique<cutu_reader> (*this_cu, *per_objfile, nullptr, false,
+					 language_minimal,
+					 storage->get_abbrev_table_cache ());
 
       if (new_reader->is_dummy ())
 	return;
@@ -3545,68 +3488,18 @@  void
 cooked_index_worker_debug_info::process_type_units
   (dwarf2_per_objfile *per_objfile, cooked_index_worker_result *storage)
 {
-  struct tu_stats *tu_stats = &per_objfile->per_bfd->tu_stats;
-  abbrev_table_up abbrev_table;
-
   if (per_objfile->per_bfd->num_type_units == 0)
     return;
 
-  /* TUs typically share abbrev tables, and there can be way more TUs than
-     abbrev tables.  Sort by abbrev table to reduce the number of times we
-     read each abbrev table in.
-     Alternatives are to punt or to maintain a cache of abbrev tables.
-     This is simpler and efficient enough for now.
-
-     Later we group TUs by their DW_AT_stmt_list value (as this defines the
-     symtab to use).  Typically TUs with the same abbrev offset have the same
-     stmt_list value too so in practice this should work well.
-
-     The basic algorithm here is:
-
-      sort TUs by abbrev table
-      for each TU with same abbrev table:
-	read abbrev table if first user
-	read TU top level DIE
-	  [IWBN if DWO skeletons had DW_AT_stmt_list]
-	call FUNC  */
-
-  dwarf_read_debug_printf ("Building type unit groups ...");
-
-  /* Sort in a separate table to maintain the order of all_units
-     for .gdb_index: TU indices directly index all_type_units.  */
-  std::vector<tu_abbrev_offset> sorted_by_abbrev;
-  sorted_by_abbrev.reserve (per_objfile->per_bfd->num_type_units);
-
   for (const auto &cu : per_objfile->per_bfd->all_units)
-    if (cu->is_debug_types ())
-      {
-	auto sig_type = static_cast<signatured_type *> (cu.get ());
-	sect_offset abbrev_offset
-	  = read_abbrev_offset (per_objfile, sig_type->section (),
-				sig_type->sect_off ());
-	sorted_by_abbrev.emplace_back (sig_type, abbrev_offset);
-      }
-
-  std::sort (sorted_by_abbrev.begin (), sorted_by_abbrev.end ());
-
-  sect_offset abbrev_offset = (sect_offset) ~(unsigned) 0;
-
-  for (const tu_abbrev_offset &tu : sorted_by_abbrev)
     {
-      /* Switch to the next abbrev table if necessary.  */
-      if (abbrev_table == NULL
-	  || tu.abbrev_offset != abbrev_offset)
-	{
-	  abbrev_offset = tu.abbrev_offset;
-	  per_objfile->per_bfd->abbrev.read (per_objfile->objfile);
-	  abbrev_table =
-	    abbrev_table::read (&per_objfile->per_bfd->abbrev, abbrev_offset);
-	  ++tu_stats->nr_uniq_abbrev_tables;
-	}
+      if (!cu->is_debug_types ())
+	 continue;
+
+      auto sig_type = static_cast<signatured_type *> (cu.get ());
+      cutu_reader reader (*sig_type, *per_objfile, nullptr, false,
+			  language_minimal, storage->get_abbrev_table_cache ());
 
-      cutu_reader reader (*tu.sig_type, *per_objfile,
-			  abbrev_table.get (), nullptr, false,
-			  language_minimal);
       if (!reader.is_dummy ())
 	storage->catch_error ([&] ()
 	  {
@@ -3639,8 +3532,8 @@  cooked_index_worker_debug_info::process_skeletonless_type_unit
   fill_in_sig_entry_from_dwo_entry (per_objfile, *sig_type_it, dwo_unit);
 
   /* This does the job that build_type_psymtabs would have done.  */
-  cutu_reader reader (**sig_type_it, *per_objfile, nullptr, nullptr, false,
-		      language_minimal);
+  cutu_reader reader (**sig_type_it, *per_objfile, nullptr, false,
+		      language_minimal, storage->get_abbrev_table_cache ());
   if (!reader.is_dummy ())
     process_type_unit (&reader, storage);
 }
@@ -4225,8 +4118,9 @@  load_full_comp_unit (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile,
   gdb_assert (!this_cu->is_debug_types ());
   gdb_assert (per_objfile->get_cu (this_cu) == nullptr);
 
-  cutu_reader reader (*this_cu, *per_objfile, nullptr, nullptr, skip_partial,
-		      pretend_language);
+  abbrev_table_cache abbrev_table_cache;
+  cutu_reader reader (*this_cu, *per_objfile, nullptr, skip_partial,
+		      pretend_language, abbrev_table_cache);
   if (reader.is_dummy ())
     return;
 
@@ -6381,6 +6275,7 @@  cutu_reader::create_dwo_unit_hash_tables (dwo_file &dwo_file,
 			   section.get_file_name ());
 
   const gdb_byte *end_ptr = info_ptr + section.size;
+  abbrev_table_cache abbrev_table_cache;
 
   while (info_ptr < end_ptr)
     {
@@ -6421,7 +6316,7 @@  cutu_reader::create_dwo_unit_hash_tables (dwo_file &dwo_file,
 	  dwarf2_per_cu per_cu (&per_bfd, &section, sect_off, length,
 				false /* is_dwz */);
 	  cutu_reader reader (per_cu, per_objfile, language_minimal,
-			      skeleton_cu, dwo_file);
+			      skeleton_cu, dwo_file, abbrev_table_cache);
 
 	  std::optional<ULONGEST> opt_signature
 	    = lookup_dwo_id (reader.cu (), reader.top_level_die ());
@@ -15565,8 +15460,9 @@  dwarf2_read_addr_index (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile,
     }
   else
     {
-      cutu_reader reader (*per_cu, *per_objfile, nullptr, nullptr, false,
-			  language_minimal);
+      abbrev_table_cache abbrev_table_cache;
+      cutu_reader reader (*per_cu, *per_objfile, nullptr, false,
+			  language_minimal, abbrev_table_cache);
       addr_base = reader.cu ()->addr_base;
       addr_size = reader.cu ()->header.addr_size;
     }
@@ -18207,8 +18103,9 @@  read_signatured_type (signatured_type *sig_type,
   gdb_assert (sig_type->is_debug_types ());
   gdb_assert (per_objfile->get_cu (sig_type) == nullptr);
 
-  cutu_reader reader (*sig_type, *per_objfile, nullptr, nullptr, false,
-		      language_minimal);
+  abbrev_table_cache abbrev_table_cache;
+  cutu_reader reader (*sig_type, *per_objfile, nullptr, false,
+		      language_minimal, abbrev_table_cache);
 
   if (!reader.is_dummy ())
     {
@@ -18673,8 +18570,9 @@  dwarf2_per_cu::ensure_lang (dwarf2_per_objfile *per_objfile)
 
   /* Constructing this object will set the language as a side
      effect.  */
-  cutu_reader reader (*this, *per_objfile, nullptr, per_objfile->get_cu (this),
-		      true, language_minimal, nullptr);
+  abbrev_table_cache abbrev_table_cache;
+  cutu_reader reader (*this, *per_objfile, per_objfile->get_cu (this),
+		      true, language_minimal, abbrev_table_cache);
 }
 
 /* Return the unit from ALL_UNITS that potentially contains TARGET.
@@ -18754,8 +18652,11 @@  dwarf2_find_containing_unit (const section_and_offset &target,
      Even though it should happen too often, it could be replaced with
      something more lightweight that has the same effect.  */
   if (!per_cu->length_is_set ())
-    cutu_reader (*per_cu, *per_objfile, nullptr, nullptr, false,
-		 language_minimal);
+    {
+      abbrev_table_cache abbrev_table_cache;
+      cutu_reader (*per_cu, *per_objfile, nullptr, false, language_minimal,
+		   abbrev_table_cache);
+    }
 
   /* Now we can check if the target section offset is within PER_CU's range.  */
   if (target.offset < per_cu->sect_off ()
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 1e81bb2056df..c4b4e39255d4 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -40,7 +40,6 @@  extern struct cmd_list_element *show_dwarf_cmdlist;
 
 struct tu_stats
 {
-  int nr_uniq_abbrev_tables = 0;
   int nr_symtabs = 0;
   int nr_symtab_sharers = 0;
   int nr_stmt_less_type_units = 0;
@@ -989,17 +988,17 @@  class cutu_reader
 
   cutu_reader (dwarf2_per_cu &this_cu,
 	       dwarf2_per_objfile &per_objfile,
-	       const struct abbrev_table *abbrev_table,
 	       dwarf2_cu *existing_cu,
 	       bool skip_partial,
 	       enum language pretend_language,
-	       const abbrev_table_cache *abbrev_cache = nullptr);
+	       abbrev_table_cache &abbrev_cache);
 
   cutu_reader (dwarf2_per_cu &this_cu,
 	       dwarf2_per_objfile &per_objfile,
 	       enum language pretend_language,
 	       struct dwarf2_cu &parent_cu,
-	       struct dwo_file &dwo_file);
+	       struct dwo_file &dwo_file,
+	       abbrev_table_cache &abbrev_table_cache);
 
   DISABLE_COPY_AND_ASSIGN (cutu_reader);
 
@@ -1027,13 +1026,6 @@  class cutu_reader
   /* Release the CU created by this cutu_reader.  */
   dwarf2_cu_up release_cu ();
 
-  /* Release the abbrev table, transferring ownership to the
-     caller.  */
-  abbrev_table_up release_abbrev_table ()
-  {
-    return std::move (m_abbrev_table_holder);
-  }
-
   /* Read all DIES of the debug info section in memory.  */
   void read_all_dies ();
 
@@ -1058,16 +1050,18 @@  class cutu_reader
 
   void init_cu_die_reader (dwarf2_cu *cu, dwarf2_section_info *section,
 			   struct dwo_file *dwo_file,
-			   const struct abbrev_table *abbrev_table);
+			   const abbrev_table &abbrev_table);
 
   void init_tu_and_read_dwo_dies (dwarf2_per_cu *this_cu,
 				  dwarf2_per_objfile *per_objfile,
 				  dwarf2_cu *existing_cu,
-				  enum language pretend_language);
+				  enum language pretend_language,
+				  abbrev_table_cache &abbrev_table_cache);
 
   void read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit,
 			       die_info *stub_comp_unit_die,
-			       const char *stub_comp_dir);
+			       const char *stub_comp_dir,
+			       abbrev_table_cache &abbrev_table_cache);
 
   void prepare_one_comp_unit (struct dwarf2_cu *cu,
 			      enum language pretend_language);
@@ -1144,12 +1138,6 @@  class cutu_reader
   bool m_dummy_p = false;
 
   dwarf2_cu_up m_new_cu;
-
-  /* The ordinary abbreviation table.  */
-  abbrev_table_up m_abbrev_table_holder;
-
-  /* The DWO abbreviation table.  */
-  abbrev_table_up m_dwo_abbrev_table;
 };
 
 /* Converts DWARF language names to GDB language names.  */