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

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