Blame SOURCES/gdb-rhbz1560010-fix-assertion-symbol-language-dict-language-1of5.patch

4c2ad1
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
4c2ad1
From: Sergio Durigan Junior <sergiodj@redhat.com>
4c2ad1
Date: Fri, 11 Jan 2019 10:52:17 -0500
4c2ad1
Subject: 
4c2ad1
 gdb-rhbz1560010-fix-assertion-symbol-language-dict-language-1of5.patch
4c2ad1
4c2ad1
;; Fix 'Assertion `SYMBOL_LANGUAGE (sym) == DICT_LANGUAGE (dict)->la_language' failed.'
4c2ad1
;; Keith Seitz, RHBZ#1560010.
4c2ad1
4c2ad1
gdb/23712: Introduce multidictionary's
4c2ad1
4c2ad1
gdb/23712 is a new manifestation of the now-infamous (at least to me)
4c2ad1
symtab/23010 assertion failure (DICT_LANGUAGE == SYMBOL_LANGAUGE).
4c2ad1
4c2ad1
An example of the problem (using test case from symtab/23010):
4c2ad1
4c2ad1
Reading symbols from /home/rdiez/rdiez/arduino/JtagDue/BuildOutput/JtagDue-obj-release/firmware.elf...done.
4c2ad1
(gdb) p SysTick_Handler
4c2ad1
dwarf2read.c:9715: internal-error: void dw2_add_symbol_to_list(symbol*, pending**): Assertion `(*listhead) == NULL || (SYMBOL_LANGUAGE ((*listhead)->symbol[0]) == SYMBOL_LANGUAGE (symbol))' failed.
4c2ad1
A problem internal to GDB has been detected,
4c2ad1
further debugging may prove unreliable.
4c2ad1
Quit this debugging session? (y or n)
4c2ad1
4c2ad1
This assertion was added specifically to catch this condition (of adding
4c2ad1
symbols of different languages to a single pending list).
4c2ad1
4c2ad1
The problems we're now seeing on systems utilizing DWARF debugging seem to
4c2ad1
be caused by the use of LTO, which adds a CU with an artificial DIE of
4c2ad1
language C99 which references DIEs in other CUs of language C++.
4c2ad1
4c2ad1
Thus, we create a dictionary containing symbols of C99 but end up
4c2ad1
stuffing C++ symbols into it, and the dw2_add_symbol_to_list triggers.
4c2ad1
4c2ad1
The approach taken here to fix this is to introduce multi-language
4c2ad1
dictionaries to "replace" the standard, single-language dictionaries
4c2ad1
used today.
4c2ad1
4c2ad1
Note to reviewers: This patch introduces some temporary functions to
4c2ad1
aide with review.  This and other artifacts (such as "See dictionary.h"
4c2ad1
which appear incorrect) will all be valid at the end of the series.
4c2ad1
4c2ad1
This first patch introduces the new multidictionary and its API (which
4c2ad1
is, by design, identical to the old dictionary interface).  It also
4c2ad1
mutates dict_create_hashed and dict_create_linear so that they take
4c2ad1
a std::vector instead of the usual struct pending linked list.  This will
4c2ad1
be needed later on.
4c2ad1
4c2ad1
This patch does /not/ actually enable multidictionary's.  That is left
4c2ad1
for a subsequent patch in the series.
4c2ad1
4c2ad1
I've done exhaustive performance testing with this approach, and I've
4c2ad1
attempted to minimize the overhead for the (overwhelmingly) most common
4c2ad1
one-language scenario.
4c2ad1
4c2ad1
On average, a -g3 -O0 GDB (the one we developers use) will see
4c2ad1
approximately a 4% slowdown when initially reading symbols. [I've
4c2ad1
tested only GDB and firefox with -readnow.]  When using -O2, this
4c2ad1
difference shrinks to ~0.5%.  Since a number of runs with these
4c2ad1
patches actually run /faster/ than unpatched GDB, I conclude that
4c2ad1
these tests have at least a 0.5% error margin.
4c2ad1
4c2ad1
On our own gdb.perf test suite, again, results appear to be pretty
4c2ad1
negligible.  Differences to unpatched GDB range from -7.8% (yes,
4c2ad1
patched version is again faster than unpatched) to 27%.  All tests
4c2ad1
lying outside "negligible," such as the 27% slowdown, involve a total
4c2ad1
run time of 0.0007 (or less) with smaller numbers of CUs/DSOs (usually 10
4c2ad1
or 100).  In all cases, the follow-up tests with more CUs/DSOs is never
4c2ad1
more than 3% difference to the baseline, unpatched GDB.
4c2ad1
4c2ad1
In my opinion, these results are satisfactory.
4c2ad1
4c2ad1
gdb/ChangeLog:
4c2ad1
4c2ad1
	PR gdb/23712
4c2ad1
	PR symtab/23010
4c2ad1
	* dictionary.c: Include unordered_map.
4c2ad1
	(pending_to_vector): New function.
4c2ad1
	(dict_create_hashed_1, dict_create_linear_1, dict_add_pending_1):
4c2ad1
	Rewrite the non-"_1" functions to take vector instead
4c2ad1
	of linked list.
4c2ad1
	(dict_create_hashed, dict_create_linear, dict_add_pending): Use the
4c2ad1
	"new" _1 versions of the same name.
4c2ad1
	(multidictionary): Define.
4c2ad1
	(std::hash
4c2ad1
	(collate_pending_symbols_by_language, mdict_create_hashed)
4c2ad1
	(mdict_create_hashed_expandable, mdict_create_linear)
4c2ad1
	(mdict_create_linear_expandable, mdict_free)
4c2ad1
	(find_language_dictionary, create_new_language_dictionary)
4c2ad1
	(mdict_add_symbol, mdict_add_pending, mdict_iterator_first)
4c2ad1
	(mdict_iterator_next, mdict_iter_match_first, mdict_iter_match_next)
4c2ad1
	(mdict_size, mdict_empty): New functions.
4c2ad1
	* dictionary.h (mdict_iterator): Define.
4c2ad1
4c2ad1
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
4c2ad1
--- a/gdb/dictionary.c
4c2ad1
+++ b/gdb/dictionary.c
4c2ad1
@@ -27,6 +27,7 @@
4c2ad1
 #include "buildsym.h"
4c2ad1
 #include "dictionary.h"
4c2ad1
 #include "safe-ctype.h"
4c2ad1
+#include <unordered_map>
4c2ad1
 
4c2ad1
 /* This file implements dictionaries, which are tables that associate
4c2ad1
    symbols to names.  They are represented by an opaque type 'struct
4c2ad1
@@ -341,53 +342,66 @@ static void insert_symbol_hashed (struct dictionary *dict,
4c2ad1
 
4c2ad1
 static void expand_hashtable (struct dictionary *dict);
4c2ad1
 
4c2ad1
+/* A function to convert a linked list into a vector.  */
4c2ad1
+
4c2ad1
+static std::vector<symbol *>
4c2ad1
+pending_to_vector (const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  std::vector<symbol *> symlist;
4c2ad1
+
4c2ad1
+  for (const struct pending *list_counter = symbol_list;
4c2ad1
+       list_counter != nullptr; list_counter = list_counter->next)
4c2ad1
+    {
4c2ad1
+      for (int i = list_counter->nsyms - 1; i >= 0; --i)
4c2ad1
+	symlist.push_back (list_counter->symbol[i]);
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return symlist;
4c2ad1
+}
4c2ad1
+
4c2ad1
 /* The creation functions.  */
4c2ad1
 
4c2ad1
-/* See dictionary.h.  */
4c2ad1
+/* A function to transition dict_create_hashed to new API.  */
4c2ad1
 
4c2ad1
-struct dictionary *
4c2ad1
-dict_create_hashed (struct obstack *obstack,
4c2ad1
-		    enum language language,
4c2ad1
-		    const struct pending *symbol_list)
4c2ad1
+static struct dictionary *
4c2ad1
+dict_create_hashed_1 (struct obstack *obstack,
4c2ad1
+		      enum language language,
4c2ad1
+		      const std::vector<symbol *> &symbol_list)
4c2ad1
 {
4c2ad1
-  struct dictionary *retval;
4c2ad1
-  int nsyms = 0, nbuckets, i;
4c2ad1
-  struct symbol **buckets;
4c2ad1
-  const struct pending *list_counter;
4c2ad1
-
4c2ad1
-  retval = XOBNEW (obstack, struct dictionary);
4c2ad1
+  /* Allocate the dictionary.  */
4c2ad1
+  struct dictionary *retval = XOBNEW (obstack, struct dictionary);
4c2ad1
   DICT_VECTOR (retval) = &dict_hashed_vector;
4c2ad1
   DICT_LANGUAGE (retval) = language_def (language);
4c2ad1
 
4c2ad1
-  /* Calculate the number of symbols, and allocate space for them.  */
4c2ad1
-  for (list_counter = symbol_list;
4c2ad1
-       list_counter != NULL;
4c2ad1
-       list_counter = list_counter->next)
4c2ad1
-    {
4c2ad1
-      nsyms += list_counter->nsyms;
4c2ad1
-    }
4c2ad1
-  nbuckets = DICT_HASHTABLE_SIZE (nsyms);
4c2ad1
+  /* Allocate space for symbols.  */
4c2ad1
+  int nsyms = symbol_list.size ();
4c2ad1
+  int nbuckets = DICT_HASHTABLE_SIZE (nsyms);
4c2ad1
   DICT_HASHED_NBUCKETS (retval) = nbuckets;
4c2ad1
-  buckets = XOBNEWVEC (obstack, struct symbol *, nbuckets);
4c2ad1
+  struct symbol **buckets = XOBNEWVEC (obstack, struct symbol *, nbuckets);
4c2ad1
   memset (buckets, 0, nbuckets * sizeof (struct symbol *));
4c2ad1
   DICT_HASHED_BUCKETS (retval) = buckets;
4c2ad1
 
4c2ad1
   /* Now fill the buckets.  */
4c2ad1
-  for (list_counter = symbol_list;
4c2ad1
-       list_counter != NULL;
4c2ad1
-       list_counter = list_counter->next)
4c2ad1
-    {
4c2ad1
-      for (i = list_counter->nsyms - 1; i >= 0; --i)
4c2ad1
-	{
4c2ad1
-	  insert_symbol_hashed (retval, list_counter->symbol[i]);
4c2ad1
-	}
4c2ad1
-    }
4c2ad1
+  for (const auto &sym : symbol_list)
4c2ad1
+    insert_symbol_hashed (retval, sym);
4c2ad1
 
4c2ad1
   return retval;
4c2ad1
 }
4c2ad1
 
4c2ad1
 /* See dictionary.h.  */
4c2ad1
 
4c2ad1
+struct dictionary *
4c2ad1
+dict_create_hashed (struct obstack *obstack,
4c2ad1
+		    enum language language,
4c2ad1
+		    const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  std::vector<symbol *> symlist = pending_to_vector (symbol_list);
4c2ad1
+
4c2ad1
+  return dict_create_hashed_1 (obstack, language, symlist);
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
 extern struct dictionary *
4c2ad1
 dict_create_hashed_expandable (enum language language)
4c2ad1
 {
4c2ad1
@@ -403,52 +417,45 @@ dict_create_hashed_expandable (enum language language)
4c2ad1
   return retval;
4c2ad1
 }
4c2ad1
 
4c2ad1
-/* See dictionary.h.  */
4c2ad1
+/* A function to transition dict_create_linear to new API.  */
4c2ad1
 
4c2ad1
-struct dictionary *
4c2ad1
-dict_create_linear (struct obstack *obstack,
4c2ad1
-		    enum language language,
4c2ad1
-		    const struct pending *symbol_list)
4c2ad1
+static struct dictionary *
4c2ad1
+dict_create_linear_1 (struct obstack *obstack,
4c2ad1
+		      enum language language,
4c2ad1
+		      const std::vector<symbol *> &symbol_list)
4c2ad1
 {
4c2ad1
-  struct dictionary *retval;
4c2ad1
-  int nsyms = 0, i, j;
4c2ad1
-  struct symbol **syms;
4c2ad1
-  const struct pending *list_counter;
4c2ad1
-
4c2ad1
-  retval = XOBNEW (obstack, struct dictionary);
4c2ad1
+  struct dictionary *retval = XOBNEW (obstack, struct dictionary);
4c2ad1
   DICT_VECTOR (retval) = &dict_linear_vector;
4c2ad1
   DICT_LANGUAGE (retval) = language_def (language);
4c2ad1
 
4c2ad1
-  /* Calculate the number of symbols, and allocate space for them.  */
4c2ad1
-  for (list_counter = symbol_list;
4c2ad1
-       list_counter != NULL;
4c2ad1
-       list_counter = list_counter->next)
4c2ad1
-    {
4c2ad1
-      nsyms += list_counter->nsyms;
4c2ad1
-    }
4c2ad1
+  /* Allocate space for symbols.  */
4c2ad1
+  int nsyms = symbol_list.size ();
4c2ad1
   DICT_LINEAR_NSYMS (retval) = nsyms;
4c2ad1
-  syms = XOBNEWVEC (obstack, struct symbol *, nsyms );
4c2ad1
+  struct symbol **syms = XOBNEWVEC (obstack, struct symbol *, nsyms);
4c2ad1
   DICT_LINEAR_SYMS (retval) = syms;
4c2ad1
 
4c2ad1
-  /* Now fill in the symbols.  Start filling in from the back, so as
4c2ad1
-     to preserve the original order of the symbols.  */
4c2ad1
-  for (list_counter = symbol_list, j = nsyms - 1;
4c2ad1
-       list_counter != NULL;
4c2ad1
-       list_counter = list_counter->next)
4c2ad1
-    {
4c2ad1
-      for (i = list_counter->nsyms - 1;
4c2ad1
-	   i >= 0;
4c2ad1
-	   --i, --j)
4c2ad1
-	{
4c2ad1
-	  syms[j] = list_counter->symbol[i];
4c2ad1
-	}
4c2ad1
-    }
4c2ad1
+  /* Now fill in the symbols.  */
4c2ad1
+  int idx = nsyms - 1;
4c2ad1
+  for (const auto &sym : symbol_list)
4c2ad1
+    syms[idx--] = sym;
4c2ad1
 
4c2ad1
   return retval;
4c2ad1
 }
4c2ad1
 
4c2ad1
 /* See dictionary.h.  */
4c2ad1
 
4c2ad1
+struct dictionary *
4c2ad1
+dict_create_linear (struct obstack *obstack,
4c2ad1
+		    enum language language,
4c2ad1
+		    const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  std::vector<symbol *> symlist = pending_to_vector (symbol_list);
4c2ad1
+
4c2ad1
+  return dict_create_linear_1 (obstack, language, symlist);
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
 struct dictionary *
4c2ad1
 dict_create_linear_expandable (enum language language)
4c2ad1
 {
4c2ad1
@@ -483,20 +490,26 @@ dict_add_symbol (struct dictionary *dict, struct symbol *sym)
4c2ad1
   (DICT_VECTOR (dict))->add_symbol (dict, sym);
4c2ad1
 }
4c2ad1
 
4c2ad1
+/* A function to transition dict_add_pending to new API.  */
4c2ad1
+
4c2ad1
+static void
4c2ad1
+dict_add_pending_1 (struct dictionary *dict,
4c2ad1
+		    const std::vector<symbol *> &symbol_list)
4c2ad1
+{
4c2ad1
+  /* Preserve ordering by reversing the list.  */
4c2ad1
+  for (auto sym = symbol_list.rbegin (); sym != symbol_list.rend (); ++sym)
4c2ad1
+    dict_add_symbol (dict, *sym);
4c2ad1
+}
4c2ad1
+
4c2ad1
 /* Utility to add a list of symbols to a dictionary.
4c2ad1
    DICT must be an expandable dictionary.  */
4c2ad1
 
4c2ad1
 void
4c2ad1
 dict_add_pending (struct dictionary *dict, const struct pending *symbol_list)
4c2ad1
 {
4c2ad1
-  const struct pending *list;
4c2ad1
-  int i;
4c2ad1
+  std::vector<symbol *> symlist = pending_to_vector (symbol_list);
4c2ad1
 
4c2ad1
-  for (list = symbol_list; list != NULL; list = list->next)
4c2ad1
-    {
4c2ad1
-      for (i = 0; i < list->nsyms; ++i)
4c2ad1
-	dict_add_symbol (dict, list->symbol[i]);
4c2ad1
-    }
4c2ad1
+  dict_add_pending_1 (dict, symlist);
4c2ad1
 }
4c2ad1
 
4c2ad1
 /* Initialize ITERATOR to point at the first symbol in DICT, and
4c2ad1
@@ -929,3 +942,408 @@ add_symbol_linear_expandable (struct dictionary *dict,
4c2ad1
 
4c2ad1
   DICT_LINEAR_SYM (dict, nsyms - 1) = sym;
4c2ad1
 }
4c2ad1
+
4c2ad1
+/* Multi-language dictionary support.  */
4c2ad1
+
4c2ad1
+/* The structure describing a multi-language dictionary.  */
4c2ad1
+
4c2ad1
+struct multidictionary
4c2ad1
+{
4c2ad1
+  /* An array of dictionaries, one per language.  All dictionaries
4c2ad1
+     must be of the same type.  This should be free'd for expandable
4c2ad1
+     dictionary types.  */
4c2ad1
+  struct dictionary **dictionaries;
4c2ad1
+
4c2ad1
+  /* The number of language dictionaries currently allocated.
4c2ad1
+     Only used for expandable dictionaries.  */
4c2ad1
+  unsigned short n_allocated_dictionaries;
4c2ad1
+};
4c2ad1
+
4c2ad1
+/* A hasher for enum language.  Injecting this into std is a convenience
4c2ad1
+   when using unordered_map with C++11.  */
4c2ad1
+
4c2ad1
+namespace std
4c2ad1
+{
4c2ad1
+  template<> struct hash<enum language>
4c2ad1
+  {
4c2ad1
+    typedef enum language argument_type;
4c2ad1
+    typedef std::size_t result_type;
4c2ad1
+
4c2ad1
+    result_type operator() (const argument_type &l) const noexcept
4c2ad1
+    {
4c2ad1
+      return static_cast<result_type> (l);
4c2ad1
+    }
4c2ad1
+  };
4c2ad1
+} /* namespace std */
4c2ad1
+
4c2ad1
+/* A helper function to collate symbols on the pending list by language.  */
4c2ad1
+
4c2ad1
+static std::unordered_map<enum language, std::vector<symbol *>>
4c2ad1
+collate_pending_symbols_by_language (const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  std::unordered_map<enum language, std::vector<symbol *>> nsyms;
4c2ad1
+
4c2ad1
+  for (const struct pending *list_counter = symbol_list;
4c2ad1
+       list_counter != nullptr; list_counter = list_counter->next)
4c2ad1
+    {
4c2ad1
+      for (int i = list_counter->nsyms - 1; i >= 0; --i)
4c2ad1
+	{
4c2ad1
+	  enum language language = SYMBOL_LANGUAGE (list_counter->symbol[i]);
4c2ad1
+	  nsyms[language].push_back (list_counter->symbol[i]);
4c2ad1
+	}
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return nsyms;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct multidictionary *
4c2ad1
+mdict_create_hashed (struct obstack *obstack,
4c2ad1
+		     const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  struct multidictionary *retval
4c2ad1
+    = XOBNEW (obstack, struct multidictionary);
4c2ad1
+  std::unordered_map<enum language, std::vector<symbol *>> nsyms
4c2ad1
+    = collate_pending_symbols_by_language (symbol_list);
4c2ad1
+
4c2ad1
+  /* Loop over all languages and create/populate dictionaries.  */
4c2ad1
+  retval->dictionaries
4c2ad1
+    = XOBNEWVEC (obstack, struct dictionary *, nsyms.size ());
4c2ad1
+  retval->n_allocated_dictionaries = nsyms.size ();
4c2ad1
+
4c2ad1
+  int idx = 0;
4c2ad1
+  for (const auto &pair : nsyms)
4c2ad1
+    {
4c2ad1
+      enum language language = pair.first;
4c2ad1
+      std::vector<symbol *> symlist = pair.second;
4c2ad1
+
4c2ad1
+      retval->dictionaries[idx++]
4c2ad1
+	= dict_create_hashed_1 (obstack, language, symlist);
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return retval;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct multidictionary *
4c2ad1
+mdict_create_hashed_expandable (enum language language)
4c2ad1
+{
4c2ad1
+  struct multidictionary *retval = XNEW (struct multidictionary);
4c2ad1
+
4c2ad1
+  /* We have no symbol list to populate, but we create an empty
4c2ad1
+     dictionary of the requested language to populate later.  */
4c2ad1
+  retval->n_allocated_dictionaries = 1;
4c2ad1
+  retval->dictionaries = XNEW (struct dictionary *);
4c2ad1
+  retval->dictionaries[0] = dict_create_hashed_expandable (language);
4c2ad1
+
4c2ad1
+  return retval;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct multidictionary *
4c2ad1
+mdict_create_linear (struct obstack *obstack,
4c2ad1
+		     const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  struct multidictionary *retval
4c2ad1
+    = XOBNEW (obstack, struct multidictionary);
4c2ad1
+  std::unordered_map<enum language, std::vector<symbol *>> nsyms
4c2ad1
+    = collate_pending_symbols_by_language (symbol_list);
4c2ad1
+
4c2ad1
+  /* Loop over all languages and create/populate dictionaries.  */
4c2ad1
+  retval->dictionaries
4c2ad1
+    = XOBNEWVEC (obstack, struct dictionary *, nsyms.size ());
4c2ad1
+  retval->n_allocated_dictionaries = nsyms.size ();
4c2ad1
+
4c2ad1
+  int idx = 0;
4c2ad1
+  for (const auto &pair : nsyms)
4c2ad1
+    {
4c2ad1
+      enum language language = pair.first;
4c2ad1
+      std::vector<symbol *> symlist = pair.second;
4c2ad1
+
4c2ad1
+      retval->dictionaries[idx++]
4c2ad1
+	= dict_create_linear_1 (obstack, language, symlist);
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return retval;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct multidictionary *
4c2ad1
+mdict_create_linear_expandable (enum language language)
4c2ad1
+{
4c2ad1
+  struct multidictionary *retval = XNEW (struct multidictionary);
4c2ad1
+
4c2ad1
+  /* We have no symbol list to populate, but we create an empty
4c2ad1
+     dictionary to populate later.  */
4c2ad1
+  retval->n_allocated_dictionaries = 1;
4c2ad1
+  retval->dictionaries = XNEW (struct dictionary *);
4c2ad1
+  retval->dictionaries[0] = dict_create_linear_expandable (language);
4c2ad1
+
4c2ad1
+  return retval;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+void
4c2ad1
+mdict_free (struct multidictionary *mdict)
4c2ad1
+{
4c2ad1
+  /* Grab the type of dictionary being used.  */
4c2ad1
+  enum dict_type type = mdict->dictionaries[0]->vector->type;
4c2ad1
+
4c2ad1
+  /* Loop over all dictionaries and free them.  */
4c2ad1
+  for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    dict_free (mdict->dictionaries[idx]);
4c2ad1
+
4c2ad1
+  /* Free the dictionary list, if needed.  */
4c2ad1
+  switch (type)
4c2ad1
+    {
4c2ad1
+    case DICT_HASHED:
4c2ad1
+    case DICT_LINEAR:
4c2ad1
+      /* Memory was allocated on an obstack when created.  */
4c2ad1
+      break;
4c2ad1
+
4c2ad1
+    case DICT_HASHED_EXPANDABLE:
4c2ad1
+    case DICT_LINEAR_EXPANDABLE:
4c2ad1
+      xfree (mdict->dictionaries);
4c2ad1
+      break;
4c2ad1
+    }
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* Helper function to find the dictionary associated with LANGUAGE
4c2ad1
+   or NULL if there is no dictionary of that language.  */
4c2ad1
+
4c2ad1
+static struct dictionary *
4c2ad1
+find_language_dictionary (const struct multidictionary *mdict,
4c2ad1
+			  enum language language)
4c2ad1
+{
4c2ad1
+  for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    {
4c2ad1
+      if (DICT_LANGUAGE (mdict->dictionaries[idx])->la_language == language)
4c2ad1
+	return mdict->dictionaries[idx];
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return nullptr;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* Create a new language dictionary for LANGUAGE and add it to the
4c2ad1
+   multidictionary MDICT's list of dictionaries.  If MDICT is not
4c2ad1
+   based on expandable dictionaries, this function throws an
4c2ad1
+   internal error.  */
4c2ad1
+
4c2ad1
+static struct dictionary *
4c2ad1
+create_new_language_dictionary (struct multidictionary *mdict,
4c2ad1
+				enum language language)
4c2ad1
+{
4c2ad1
+  struct dictionary *retval = nullptr;
4c2ad1
+
4c2ad1
+  /* We use the first dictionary entry to decide what create function
4c2ad1
+     to call.  Not optimal but sufficient.  */
4c2ad1
+  gdb_assert (mdict->dictionaries[0] != nullptr);
4c2ad1
+  switch (mdict->dictionaries[0]->vector->type)
4c2ad1
+    {
4c2ad1
+    case DICT_HASHED:
4c2ad1
+    case DICT_LINEAR:
4c2ad1
+      internal_error (__FILE__, __LINE__,
4c2ad1
+		      _("create_new_language_dictionary: attempted to expand "
4c2ad1
+			"non-expandable multidictionary"));
4c2ad1
+
4c2ad1
+    case DICT_HASHED_EXPANDABLE:
4c2ad1
+      retval = dict_create_hashed_expandable (language);
4c2ad1
+      break;
4c2ad1
+
4c2ad1
+    case DICT_LINEAR_EXPANDABLE:
4c2ad1
+      retval = dict_create_linear_expandable (language);
4c2ad1
+      break;
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  /* Grow the dictionary vector and save the new dictionary.  */
4c2ad1
+  mdict->dictionaries
4c2ad1
+    = (struct dictionary **) xrealloc (mdict->dictionaries,
4c2ad1
+				       (++mdict->n_allocated_dictionaries
4c2ad1
+					* sizeof (struct dictionary *)));
4c2ad1
+  mdict->dictionaries[mdict->n_allocated_dictionaries - 1] = retval;
4c2ad1
+
4c2ad1
+  return retval;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+void
4c2ad1
+mdict_add_symbol (struct multidictionary *mdict, struct symbol *sym)
4c2ad1
+{
4c2ad1
+  struct dictionary *dict
4c2ad1
+    = find_language_dictionary (mdict, SYMBOL_LANGUAGE (sym));
4c2ad1
+
4c2ad1
+  if (dict == nullptr)
4c2ad1
+    {
4c2ad1
+      /* SYM is of a new language that we haven't previously seen.
4c2ad1
+	 Create a new dictionary for it.  */
4c2ad1
+      dict = create_new_language_dictionary (mdict, SYMBOL_LANGUAGE (sym));
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  dict_add_symbol (dict, sym);
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+void
4c2ad1
+mdict_add_pending (struct multidictionary *mdict,
4c2ad1
+		   const struct pending *symbol_list)
4c2ad1
+{
4c2ad1
+  std::unordered_map<enum language, std::vector<symbol *>> nsyms
4c2ad1
+    = collate_pending_symbols_by_language (symbol_list);
4c2ad1
+
4c2ad1
+  for (const auto &pair : nsyms)
4c2ad1
+    {
4c2ad1
+      enum language language = pair.first;
4c2ad1
+      std::vector<symbol *> symlist = pair.second;
4c2ad1
+      struct dictionary *dict = find_language_dictionary (mdict, language);
4c2ad1
+
4c2ad1
+      if (dict == nullptr)
4c2ad1
+	{
4c2ad1
+	  /* The language was not previously seen.  Create a new dictionary
4c2ad1
+	     for it.  */
4c2ad1
+	  dict = create_new_language_dictionary (mdict, language);
4c2ad1
+	}
4c2ad1
+
4c2ad1
+      dict_add_pending_1 (dict, symlist);
4c2ad1
+    }
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct symbol *
4c2ad1
+mdict_iterator_first (const multidictionary *mdict,
4c2ad1
+		      struct mdict_iterator *miterator)
4c2ad1
+{
4c2ad1
+  miterator->mdict = mdict;
4c2ad1
+  miterator->current_idx = 0;
4c2ad1
+
4c2ad1
+  for (unsigned short idx = miterator->current_idx;
4c2ad1
+       idx < mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    {
4c2ad1
+      struct symbol *result
4c2ad1
+	= dict_iterator_first (mdict->dictionaries[idx], &miterator->iterator);
4c2ad1
+
4c2ad1
+      if (result != nullptr)
4c2ad1
+	{
4c2ad1
+	  miterator->current_idx = idx;
4c2ad1
+	  return result;
4c2ad1
+	}
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return nullptr;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct symbol *
4c2ad1
+mdict_iterator_next (struct mdict_iterator *miterator)
4c2ad1
+{
4c2ad1
+  struct symbol *result = dict_iterator_next (&miterator->iterator);
4c2ad1
+
4c2ad1
+  if (result != nullptr)
4c2ad1
+    return result;
4c2ad1
+
4c2ad1
+  /* The current dictionary had no matches -- move to the next
4c2ad1
+     dictionary, if any.  */
4c2ad1
+  for (unsigned short idx = ++miterator->current_idx;
4c2ad1
+       idx < miterator->mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    {
4c2ad1
+      result
4c2ad1
+	= dict_iterator_first (miterator->mdict->dictionaries[idx],
4c2ad1
+			       &miterator->iterator);
4c2ad1
+      if (result != nullptr)
4c2ad1
+	{
4c2ad1
+	  miterator->current_idx = idx;
4c2ad1
+	  return result;
4c2ad1
+	}
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return nullptr;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct symbol *
4c2ad1
+mdict_iter_match_first (const struct multidictionary *mdict,
4c2ad1
+			const lookup_name_info &name,
4c2ad1
+			struct mdict_iterator *miterator)
4c2ad1
+{
4c2ad1
+  miterator->mdict = mdict;
4c2ad1
+  miterator->current_idx = 0;
4c2ad1
+
4c2ad1
+  for (unsigned short idx = miterator->current_idx;
4c2ad1
+       idx < mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    {
4c2ad1
+      struct symbol *result
4c2ad1
+	= dict_iter_match_first (mdict->dictionaries[idx], name,
4c2ad1
+				 &miterator->iterator);
4c2ad1
+
4c2ad1
+      if (result != nullptr)
4c2ad1
+	return result;
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return nullptr;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+struct symbol *
4c2ad1
+mdict_iter_match_next (const lookup_name_info &name,
4c2ad1
+		       struct mdict_iterator *miterator)
4c2ad1
+{
4c2ad1
+  /* Search the current dictionary.  */
4c2ad1
+  struct symbol *result = dict_iter_match_next (name, &miterator->iterator);
4c2ad1
+
4c2ad1
+  if (result != nullptr)
4c2ad1
+    return result;
4c2ad1
+
4c2ad1
+  /* The current dictionary had no matches -- move to the next
4c2ad1
+     dictionary, if any.  */
4c2ad1
+  for (unsigned short idx = ++miterator->current_idx;
4c2ad1
+       idx < miterator->mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    {
4c2ad1
+      result
4c2ad1
+	= dict_iter_match_first (miterator->mdict->dictionaries[idx],
4c2ad1
+				 name, &miterator->iterator);
4c2ad1
+      if (result != nullptr)
4c2ad1
+	{
4c2ad1
+	  miterator->current_idx = idx;
4c2ad1
+	  return result;
4c2ad1
+	}
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return nullptr;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+int
4c2ad1
+mdict_size (const struct multidictionary *mdict)
4c2ad1
+{
4c2ad1
+  int size = 0;
4c2ad1
+
4c2ad1
+  for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    size += dict_size (mdict->dictionaries[idx]);
4c2ad1
+
4c2ad1
+  return size;
4c2ad1
+}
4c2ad1
+
4c2ad1
+/* See dictionary.h.  */
4c2ad1
+
4c2ad1
+bool
4c2ad1
+mdict_empty (const struct multidictionary *mdict)
4c2ad1
+{
4c2ad1
+  for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx)
4c2ad1
+    {
4c2ad1
+      if (!dict_empty (mdict->dictionaries[idx]))
4c2ad1
+	return false;
4c2ad1
+    }
4c2ad1
+
4c2ad1
+  return true;
4c2ad1
+}
4c2ad1
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
4c2ad1
--- a/gdb/dictionary.h
4c2ad1
+++ b/gdb/dictionary.h
4c2ad1
@@ -113,6 +113,21 @@ struct dict_iterator
4c2ad1
   struct symbol *current;
4c2ad1
 };
4c2ad1
 
4c2ad1
+/* The multi-language dictionary iterator.  Like dict_iterator above,
4c2ad1
+   these contents should be considered private.  */
4c2ad1
+
4c2ad1
+struct mdict_iterator
4c2ad1
+{
4c2ad1
+  /* The multidictionary with whcih this iterator is associated.  */
4c2ad1
+  const struct multidictionary *mdict;
4c2ad1
+
4c2ad1
+  /* The iterator used to iterate through individual dictionaries.  */
4c2ad1
+  struct dict_iterator iterator;
4c2ad1
+
4c2ad1
+  /* The current index of the dictionary being iterated over.  */
4c2ad1
+  unsigned short current_idx;
4c2ad1
+};
4c2ad1
+
4c2ad1
 /* Initialize ITERATOR to point at the first symbol in DICT, and
4c2ad1
    return that first symbol, or NULL if DICT is empty.  */
4c2ad1