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

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