diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index e67635f..ea41270 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,34 @@
 2019-11-22  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* mi/mi-symbol-cmds.c (mi_symbol_info): Take extra parameter, and
+	add it into the search spec.
+	(mi_info_functions_or_variables): Parse -max-results flag and pass
+	it to mi_symbol_info.
+	(mi_cmd_symbol_info_modules): Likewise.
+	(mi_cmd_symbol_info_types): Likewise.
+	* symtab.c (symbol_search::compare_search_syms): Update header
+	comment.
+	(global_symbol_searcher::is_suitable_msymbol): New function.
+	(global_symbol_searcher::expand_symtabs): New function.
+	(global_symbol_searcher::add_matching_symbols): New function.
+	(global_symbol_searcher::add_matching_msymbols): New function.
+	(global_symbol_searcher::search_symbols): Move most of the content
+	into the new functions above, and call them as needed.
+	* symtab.h: Add 'set' include.
+	(global_symbol_searcher) <set_max_seach_results>: New member
+	function.
+	(global_symbol_searcher) <m_max_search_results>: New member
+	variable.
+	(global_symbol_searcher) <expand_symtabs>: New member function.
+	(global_symbol_searcher) <add_matching_symbols>: New member
+	function.
+	(global_symbol_searcher) <add_matching_msymbols>: New member
+	function.
+	(global_symbol_searcher) <is_suitable_msymbol>: New member
+	function.
+
+2019-11-22  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* mi/mi-cmds.c (mi_cmds): Add -symbol-info-module-functions and
 	-symbol-info-module-variables entries.
 	* mi/mi-cmds.h (mi_cmd_symbol_info_module_functions): Declare.
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index ae310ea..36bde5a 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,5 +1,10 @@
 2019-11-22  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* doc/gdb.texinfo (GDB/MI Symbol Query): Add documentation of
+	-max-results to some -symbol-info-* commands.
+
+2019-11-22  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* doc/gdb.texinfo (GDB/MI Symbol Query): Document new MI command
 	-symbol-info-module-functions and -symbol-info-module-variables.
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index fb32528..e004ae3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -33886,6 +33886,7 @@
  -symbol-info-functions [--include-nondebug]
                         [--type @var{type_regexp}]
                         [--name @var{name_regexp}]
+                        [--max-results @var{limit}]
 @end smallexample
 
 @noindent
@@ -33901,6 +33902,11 @@
 to be filtered based on either the name of the function, or the type
 signature of the function.
 
+The option @code{--max-results} restricts the command to return no
+more than @var{limit} results.  If exactly @var{limit} results are
+returned then there might be additional results available if a higher
+limit is used.
+
 @subsubheading @value{GDBN} Command
 
 The corresponding @value{GDBN} command is @samp{info functions}.
@@ -34132,6 +34138,8 @@
 
 @smallexample
  -symbol-info-modules [--name @var{name_regexp}]
+                      [--max-results @var{limit}]
+
 @end smallexample
 
 @noindent
@@ -34142,6 +34150,11 @@
 The option @code{--name} allows the modules returned to be filtered
 based the name of the module.
 
+The option @code{--max-results} restricts the command to return no
+more than @var{limit} results.  If exactly @var{limit} results are
+returned then there might be additional results available if a higher
+limit is used.
+
 @subsubheading @value{GDBN} Command
 
 The corresponding @value{GDBN} command is @samp{info modules}.
@@ -34186,6 +34199,8 @@
 
 @smallexample
  -symbol-info-types [--name @var{name_regexp}]
+                    [--max-results @var{limit}]
+
 @end smallexample
 
 @noindent
@@ -34199,6 +34214,11 @@
 The option @code{--name} allows the list of types returned to be
 filtered by name.
 
+The option @code{--max-results} restricts the command to return no
+more than @var{limit} results.  If exactly @var{limit} results are
+returned then there might be additional results available if a higher
+limit is used.
+
 @subsubheading @value{GDBN} Command
 
 The corresponding @value{GDBN} command is @samp{info types}.
@@ -34246,6 +34266,8 @@
  -symbol-info-variables [--include-nondebug]
                         [--type @var{type_regexp}]
                         [--name @var{name_regexp}]
+                        [--max-results @var{limit}]
+
 @end smallexample
 
 @noindent
@@ -34261,6 +34283,11 @@
 to be filtered based on either the name of the variable, or the type
 of the variable.
 
+The option @code{--max-results} restricts the command to return no
+more than @var{limit} results.  If exactly @var{limit} results are
+returned then there might be additional results available if a higher
+limit is used.
+
 @subsubheading @value{GDBN} Command
 
 The corresponding @value{GDBN} command is @samp{info variables}.
diff --git a/gdb/mi/mi-symbol-cmds.c b/gdb/mi/mi-symbol-cmds.c
index de6d86d..1b31fc4 100644
--- a/gdb/mi/mi-symbol-cmds.c
+++ b/gdb/mi/mi-symbol-cmds.c
@@ -248,11 +248,13 @@
 
 static void
 mi_symbol_info (enum search_domain kind, const char *regexp,
-		const char *t_regexp, bool exclude_minsyms)
+		const char *t_regexp, bool exclude_minsyms,
+		int max_results)
 {
   global_symbol_searcher sym_search (kind, regexp);
   sym_search.set_symbol_type_regexp (t_regexp);
   sym_search.set_exclude_minsyms (exclude_minsyms);
+  sym_search.set_max_search_results (max_results);
   std::vector<symbol_search> symbols = sym_search.search ();
 
   mi_symbol_info_emitter emitter (current_uiout);
@@ -269,19 +271,21 @@
 static void
 mi_info_functions_or_variables (enum search_domain kind, char **argv, int argc)
 {
+  int max_results = -1;	/* -1 for unlimited.  */
   const char *regexp = nullptr;
   const char *t_regexp = nullptr;
   bool exclude_minsyms = true;
 
   enum opt
     {
-     INCLUDE_NONDEBUG_OPT, TYPE_REGEXP_OPT, NAME_REGEXP_OPT
+     INCLUDE_NONDEBUG_OPT, TYPE_REGEXP_OPT, NAME_REGEXP_OPT, MAX_RESULTS_OPT
     };
   static const struct mi_opt opts[] =
   {
     {"-include-nondebug" , INCLUDE_NONDEBUG_OPT, 0},
     {"-type", TYPE_REGEXP_OPT, 1},
     {"-name", NAME_REGEXP_OPT, 1},
+    {"-max-results", MAX_RESULTS_OPT, 1},
     { 0, 0, 0 }
   };
 
@@ -307,10 +311,19 @@
 	case NAME_REGEXP_OPT:
 	  regexp = oarg;
 	  break;
+	case MAX_RESULTS_OPT:
+	  {
+	    char *eptr = oarg;
+	    long val = strtol (oarg, &eptr, 10);
+	    if (oarg == eptr || val > INT_MAX || val < 0)
+	      error (_("invalid value for --max-results argument"));
+	    max_results = (int) val;
+	  }
+	  break;
 	}
     }
 
-  mi_symbol_info (kind, regexp, t_regexp, exclude_minsyms);
+  mi_symbol_info (kind, regexp, t_regexp, exclude_minsyms, max_results);
 }
 
 /* Core of -symbol-info-module-functions and -symbol-info-module-variables.
@@ -444,15 +457,17 @@
 void
 mi_cmd_symbol_info_modules (const char *command, char **argv, int argc)
 {
+  int max_results = -1; /* -1 for unlimited.  */
   const char *regexp = nullptr;
 
   enum opt
     {
-     NAME_REGEXP_OPT
+     NAME_REGEXP_OPT, MAX_RESULTS_OPT
     };
   static const struct mi_opt opts[] =
   {
     {"-name", NAME_REGEXP_OPT, 1},
+    {"-max-results", MAX_RESULTS_OPT, 1},
     { 0, 0, 0 }
   };
 
@@ -470,10 +485,19 @@
 	case NAME_REGEXP_OPT:
 	  regexp = oarg;
 	  break;
+	case MAX_RESULTS_OPT:
+	  {
+	    char *eptr = oarg;
+	    long val = strtol (oarg, &eptr, 10);
+	    if (oarg == eptr || val > INT_MAX || val < 0)
+	      error (_("invalid value for --max-results argument"));
+	    max_results = (int) val;
+	  }
+	  break;
 	}
     }
 
-  mi_symbol_info (MODULES_DOMAIN, regexp, nullptr, true);
+  mi_symbol_info (MODULES_DOMAIN, regexp, nullptr, true, max_results);
 }
 
 /* Implement -symbol-info-types command.  */
@@ -481,15 +505,17 @@
 void
 mi_cmd_symbol_info_types (const char *command, char **argv, int argc)
 {
+  int max_results = -1; /* -1 for unlimited.  */
   const char *regexp = nullptr;
 
   enum opt
     {
-     NAME_REGEXP_OPT
+     NAME_REGEXP_OPT, MAX_RESULTS_OPT
     };
   static const struct mi_opt opts[] =
   {
     {"-name", NAME_REGEXP_OPT, 1},
+    {"-max-results", MAX_RESULTS_OPT, 1},
     { 0, 0, 0 }
   };
 
@@ -507,10 +533,19 @@
 	case NAME_REGEXP_OPT:
 	  regexp = oarg;
 	  break;
+	case MAX_RESULTS_OPT:
+	  {
+	    char *eptr = oarg;
+	    long val = strtol (oarg, &eptr, 10);
+	    if (oarg == eptr || val > INT_MAX || val < 0)
+	      error (_("invalid value for --max-results argument"));
+	    max_results = (int) val;
+	  }
+	  break;
 	}
     }
 
-  mi_symbol_info (TYPES_DOMAIN, regexp, nullptr, true);
+  mi_symbol_info (TYPES_DOMAIN, regexp, nullptr, true, max_results);
 }
 
 /* Implement -symbol-info-variables command.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index eb6a995..6394f09 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4359,8 +4359,8 @@
   return false;
 }
 
-/* Helper function for sort_search_symbols_remove_dups and qsort.  Can only
-   sort symbols, not minimal symbols.  */
+/* Helper function for std::sort on symbol_search objects.  Can only sort
+   symbols, not minimal symbols.  */
 
 int
 symbol_search::compare_search_syms (const symbol_search &sym_a,
@@ -4422,15 +4422,239 @@
   return treg.exec (printed_sym_type_name.c_str (), 0, NULL, 0) == 0;
 }
 
+/* See symtab.h.  */
 
-/* Sort the symbols in RESULT and remove duplicates.  */
-
-static void
-sort_search_symbols_remove_dups (std::vector<symbol_search> *result)
+bool
+global_symbol_searcher::is_suitable_msymbol
+	(const enum search_domain kind, const minimal_symbol *msymbol)
 {
-  std::sort (result->begin (), result->end ());
-  result->erase (std::unique (result->begin (), result->end ()),
-		 result->end ());
+  static const enum minimal_symbol_type types[]
+    = {mst_data, mst_text};
+  static const enum minimal_symbol_type types2[]
+    = {mst_bss, mst_file_text};
+  static const enum minimal_symbol_type types3[]
+    = {mst_file_data, mst_solib_trampoline};
+  static const enum minimal_symbol_type types4[]
+    = {mst_file_bss, mst_text_gnu_ifunc};
+
+  gdb_assert (kind == VARIABLES_DOMAIN || kind == FUNCTIONS_DOMAIN);
+  gdb_assert (kind <= sizeof (types));
+
+  enum minimal_symbol_type ourtype = types[kind];
+  enum minimal_symbol_type ourtype2 = types2[kind];
+  enum minimal_symbol_type ourtype3 = types3[kind];
+  enum minimal_symbol_type ourtype4 = types4[kind];
+
+  return (MSYMBOL_TYPE (msymbol) == ourtype
+	  || MSYMBOL_TYPE (msymbol) == ourtype2
+	  || MSYMBOL_TYPE (msymbol) == ourtype3
+	  || MSYMBOL_TYPE (msymbol) == ourtype4);
+}
+
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::expand_symtabs
+	(objfile *objfile, const gdb::optional<compiled_regex> &preg) const
+{
+  enum search_domain kind = m_kind;
+  bool found_misc = false;
+
+  if (objfile->sf)
+    objfile->sf->qf->expand_symtabs_matching
+      (objfile,
+       [&] (const char *filename, bool basenames)
+       {
+	 return file_matches (filename, filenames, basenames);
+       },
+       lookup_name_info::match_any (),
+       [&] (const char *symname)
+       {
+	 return (!preg.has_value ()
+		 || preg->exec (symname, 0, NULL, 0) == 0);
+       },
+       NULL,
+       kind);
+
+  /* Here, we search through the minimal symbol tables for functions and
+     variables that match, and force their symbols to be read.  This is in
+     particular necessary for demangled variable names, which are no longer
+     put into the partial symbol tables.  The symbol will then be found
+     during the scan of symtabs later.
+
+     For functions, find_pc_symtab should succeed if we have debug info for
+     the function, for variables we have to call
+     lookup_symbol_in_objfile_from_linkage_name to determine if the
+     variable has debug info.  If the lookup fails, set found_misc so that
+     we will rescan to print any matching symbols without debug info.  We
+     only search the objfile the msymbol came from, we no longer search all
+     objfiles.  In large programs (1000s of shared libs) searching all
+     objfiles is not worth the pain.  */
+  if (filenames.empty ()
+      && (kind == VARIABLES_DOMAIN || kind == FUNCTIONS_DOMAIN))
+    {
+      for (minimal_symbol *msymbol : objfile->msymbols ())
+	{
+	  QUIT;
+
+	  if (msymbol->created_by_gdb)
+	    continue;
+
+	  if (is_suitable_msymbol (kind, msymbol))
+	    {
+	      if (!preg.has_value ()
+		  || preg->exec (MSYMBOL_NATURAL_NAME (msymbol), 0,
+				 NULL, 0) == 0)
+		{
+		  /* Note: An important side-effect of these
+		     lookup functions is to expand the symbol
+		     table if msymbol is found, for the benefit of
+		     the next loop on compunits.  */
+		  if (kind == FUNCTIONS_DOMAIN
+		      ? (find_pc_compunit_symtab
+			 (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
+			 == NULL)
+		      : (lookup_symbol_in_objfile_from_linkage_name
+			 (objfile, MSYMBOL_LINKAGE_NAME (msymbol),
+			  VAR_DOMAIN)
+			 .symbol == NULL))
+		    found_misc = true;
+		}
+	    }
+	}
+    }
+
+  return found_misc;
+}
+
+/* See symtab.h.  */
+bool
+global_symbol_searcher::add_matching_symbols
+	(objfile *objfile,
+	 const gdb::optional<compiled_regex> &preg,
+	 const gdb::optional<compiled_regex> &treg,
+	 std::set<symbol_search> *result_set) const
+{
+  enum search_domain kind = m_kind;
+
+  /* Add matching symbols (if not already present).  */
+  for (compunit_symtab *cust : objfile->compunits ())
+    {
+      const struct blockvector *bv  = COMPUNIT_BLOCKVECTOR (cust);
+
+      for (int i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
+	{
+	  struct block_iterator iter;
+	  struct symbol *sym;
+	  const struct block *b = BLOCKVECTOR_BLOCK (bv, i);
+
+	  ALL_BLOCK_SYMBOLS (b, iter, sym)
+	    {
+	      struct symtab *real_symtab = symbol_symtab (sym);
+
+	      QUIT;
+
+	      /* Check first sole REAL_SYMTAB->FILENAME.  It does
+		 not need to be a substring of symtab_to_fullname as
+		 it may contain "./" etc.  */
+	      if ((file_matches (real_symtab->filename, filenames, false)
+		   || ((basenames_may_differ
+			|| file_matches (lbasename (real_symtab->filename),
+					 filenames, true))
+		       && file_matches (symtab_to_fullname (real_symtab),
+					filenames, false)))
+		  && ((!preg.has_value ()
+		       || preg->exec (SYMBOL_NATURAL_NAME (sym), 0,
+				      NULL, 0) == 0)
+		      && ((kind == VARIABLES_DOMAIN
+			   && SYMBOL_CLASS (sym) != LOC_TYPEDEF
+			   && SYMBOL_CLASS (sym) != LOC_UNRESOLVED
+			   && SYMBOL_CLASS (sym) != LOC_BLOCK
+			   /* LOC_CONST can be used for more than
+			      just enums, e.g., c++ static const
+			      members.  We only want to skip enums
+			      here.  */
+			   && !(SYMBOL_CLASS (sym) == LOC_CONST
+				&& (TYPE_CODE (SYMBOL_TYPE (sym))
+				    == TYPE_CODE_ENUM))
+			   && (!treg.has_value ()
+			       || treg_matches_sym_type_name (*treg, sym)))
+			  || (kind == FUNCTIONS_DOMAIN
+			      && SYMBOL_CLASS (sym) == LOC_BLOCK
+			      && (!treg.has_value ()
+				  || treg_matches_sym_type_name (*treg,
+								 sym)))
+			  || (kind == TYPES_DOMAIN
+			      && SYMBOL_CLASS (sym) == LOC_TYPEDEF
+			      && SYMBOL_DOMAIN (sym) != MODULE_DOMAIN)
+			  || (kind == MODULES_DOMAIN
+			      && SYMBOL_DOMAIN (sym) == MODULE_DOMAIN
+			      && SYMBOL_LINE (sym) != 0))))
+		{
+		  if (m_max_search_results == -1
+		      || result_set->size () < m_max_search_results)
+		    {
+		      /* Match, insert if not already in the results.  */
+		      symbol_search ss (i, sym);
+		      if (result_set->find (ss) == result_set->end ())
+			result_set->insert (ss);
+		    }
+		  else
+		    return false;
+		}
+	    }
+	}
+    }
+
+  return true;
+}
+
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::add_matching_msymbols
+	(objfile *objfile, const gdb::optional<compiled_regex> &preg,
+	 std::vector<symbol_search> *results) const
+{
+  enum search_domain kind = m_kind;
+
+  for (minimal_symbol *msymbol : objfile->msymbols ())
+    {
+      QUIT;
+
+      if (msymbol->created_by_gdb)
+	continue;
+
+      if (is_suitable_msymbol (kind, msymbol))
+	{
+	  if (!preg.has_value ()
+	      || preg->exec (MSYMBOL_NATURAL_NAME (msymbol), 0,
+			     NULL, 0) == 0)
+	    {
+	      /* For functions we can do a quick check of whether the
+		 symbol might be found via find_pc_symtab.  */
+	      if (kind != FUNCTIONS_DOMAIN
+		  || (find_pc_compunit_symtab
+		      (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
+		      == NULL))
+		{
+		  if (lookup_symbol_in_objfile_from_linkage_name
+		      (objfile, MSYMBOL_LINKAGE_NAME (msymbol),
+		       VAR_DOMAIN).symbol == NULL)
+		    {
+		      /* Matching msymbol, add it to the results list.  */
+		      if (m_max_search_results == -1
+			  || (results->size () < m_max_search_results))
+			results->emplace_back (GLOBAL_BLOCK, msymbol, objfile);
+		      else
+			return false;
+		    }
+		}
+	    }
+	}
+    }
+
+  return true;
 }
 
 /* See symtab.h.  */
@@ -4438,35 +4662,11 @@
 std::vector<symbol_search>
 global_symbol_searcher::search () const
 {
-  const struct blockvector *bv;
-  const struct block *b;
-  int i = 0;
-  struct block_iterator iter;
-  struct symbol *sym;
-  int found_misc = 0;
-  static const enum minimal_symbol_type types[]
-    = {mst_data, mst_text, mst_unknown};
-  static const enum minimal_symbol_type types2[]
-    = {mst_bss, mst_file_text, mst_unknown};
-  static const enum minimal_symbol_type types3[]
-    = {mst_file_data, mst_solib_trampoline, mst_unknown};
-  static const enum minimal_symbol_type types4[]
-    = {mst_file_bss, mst_text_gnu_ifunc, mst_unknown};
-  enum minimal_symbol_type ourtype;
-  enum minimal_symbol_type ourtype2;
-  enum minimal_symbol_type ourtype3;
-  enum minimal_symbol_type ourtype4;
-  std::vector<symbol_search> result;
   gdb::optional<compiled_regex> preg;
   gdb::optional<compiled_regex> treg;
 
   gdb_assert (m_kind != ALL_DOMAIN);
 
-  ourtype = types[m_kind];
-  ourtype2 = types2[m_kind];
-  ourtype3 = types3[m_kind];
-  ourtype4 = types4[m_kind];
-
   if (m_symbol_name_regexp != NULL)
     {
       const char *symbol_name_regexp = m_symbol_name_regexp;
@@ -4519,189 +4719,36 @@
 		    _("Invalid m_symbol_namregexp"));
     }
 
-  /* Search through the partial symtabs *first* for all symbols
-     matching the m_symbol_namregexp.  That way we don't have to reproduce all of
-     the machinery below.  */
-  expand_symtabs_matching ([&] (const char *filename, bool basenames)
-			   {
-			     return file_matches (filename, filenames,
-						  basenames);
-			   },
-			   lookup_name_info::match_any (),
-			   [&] (const char *symname)
-			   {
-			     return (!preg.has_value ()
-				     || preg->exec (symname,
-						    0, NULL, 0) == 0);
-			   },
-			   NULL,
-			   m_kind);
-
-  /* Here, we search through the minimal symbol tables for functions
-     and variables that match, and force their symbols to be read.
-     This is in particular necessary for demangled variable names,
-     which are no longer put into the partial symbol tables.
-     The symbol will then be found during the scan of symtabs below.
-
-     For functions, find_pc_symtab should succeed if we have debug info
-     for the function, for variables we have to call
-     lookup_symbol_in_objfile_from_linkage_name to determine if the variable
-     has debug info.
-     If the lookup fails, set found_misc so that we will rescan to print
-     any matching symbols without debug info.
-     We only search the objfile the msymbol came from, we no longer search
-     all objfiles.  In large programs (1000s of shared libs) searching all
-     objfiles is not worth the pain.  */
-
-  if (filenames.empty () && (m_kind == VARIABLES_DOMAIN
-			     || m_kind == FUNCTIONS_DOMAIN))
-    {
-      for (objfile *objfile : current_program_space->objfiles ())
-	{
-	  for (minimal_symbol *msymbol : objfile->msymbols ())
-	    {
-	      QUIT;
-
-	      if (msymbol->created_by_gdb)
-		continue;
-
-	      if (MSYMBOL_TYPE (msymbol) == ourtype
-		  || MSYMBOL_TYPE (msymbol) == ourtype2
-		  || MSYMBOL_TYPE (msymbol) == ourtype3
-		  || MSYMBOL_TYPE (msymbol) == ourtype4)
-		{
-		  if (!preg.has_value ()
-		      || preg->exec (MSYMBOL_NATURAL_NAME (msymbol), 0,
-				     NULL, 0) == 0)
-		    {
-		      /* Note: An important side-effect of these
-			 lookup functions is to expand the symbol
-			 table if msymbol is found, for the benefit of
-			 the next loop on compunits.  */
-		      if (m_kind == FUNCTIONS_DOMAIN
-			  ? (find_pc_compunit_symtab
-			     (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
-			     == NULL)
-			  : (lookup_symbol_in_objfile_from_linkage_name
-			     (objfile, MSYMBOL_LINKAGE_NAME (msymbol),
-			      VAR_DOMAIN)
-			     .symbol == NULL))
-			found_misc = 1;
-		    }
-		}
-	    }
-	}
-    }
-
+  bool found_misc = false;
+  std::set<symbol_search> result_set;
   for (objfile *objfile : current_program_space->objfiles ())
     {
-      for (compunit_symtab *cust : objfile->compunits ())
-	{
-	  bv = COMPUNIT_BLOCKVECTOR (cust);
-	  for (i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
-	    {
-	      b = BLOCKVECTOR_BLOCK (bv, i);
-	      ALL_BLOCK_SYMBOLS (b, iter, sym)
-		{
-		  struct symtab *real_symtab = symbol_symtab (sym);
+      /* Expand symtabs within objfile that possibly contain matching
+	 symbols.  */
+      found_misc |= expand_symtabs (objfile, preg);
 
-		  QUIT;
-
-		  /* Check first sole REAL_SYMTAB->FILENAME.  It does
-		     not need to be a substring of symtab_to_fullname as
-		     it may contain "./" etc.  */
-		  if ((file_matches (real_symtab->filename, filenames, false)
-		       || ((basenames_may_differ
-			    || file_matches (lbasename (real_symtab->filename),
-					     filenames, true))
-			   && file_matches (symtab_to_fullname (real_symtab),
-					    filenames, false)))
-		      && ((!preg.has_value ()
-			   || preg->exec (SYMBOL_NATURAL_NAME (sym), 0,
-					  NULL, 0) == 0)
-			  && ((m_kind == VARIABLES_DOMAIN
-			       && SYMBOL_CLASS (sym) != LOC_TYPEDEF
-			       && SYMBOL_CLASS (sym) != LOC_UNRESOLVED
-			       && SYMBOL_CLASS (sym) != LOC_BLOCK
-			       /* LOC_CONST can be used for more than
-				  just enums, e.g., c++ static const
-				  members.  We only want to skip enums
-				  here.  */
-			       && !(SYMBOL_CLASS (sym) == LOC_CONST
-				    && (TYPE_CODE (SYMBOL_TYPE (sym))
-					== TYPE_CODE_ENUM))
-			       && (!treg.has_value ()
-				   || treg_matches_sym_type_name (*treg, sym)))
-			      || (m_kind == FUNCTIONS_DOMAIN
-				  && SYMBOL_CLASS (sym) == LOC_BLOCK
-				  && (!treg.has_value ()
-				      || treg_matches_sym_type_name (*treg,
-								     sym)))
-			      || (m_kind == TYPES_DOMAIN
-				  && SYMBOL_CLASS (sym) == LOC_TYPEDEF
-				  && SYMBOL_DOMAIN (sym) != MODULE_DOMAIN)
-			      || (m_kind == MODULES_DOMAIN
-				  && SYMBOL_DOMAIN (sym) == MODULE_DOMAIN
-				  && SYMBOL_LINE (sym) != 0))))
-		    {
-		      /* match */
-		      result.emplace_back (i, sym);
-		    }
-		}
-	    }
-	}
+      /* Find matching symbols within OBJFILE and add them in to
+	 RESULT_SET.  */
+      if (!add_matching_symbols (objfile, preg, treg, &result_set))
+	break;
     }
 
-  if (!result.empty ())
-    sort_search_symbols_remove_dups (&result);
+  /* Convert the result set into a sorted result list.  */
+  std::vector<symbol_search> result (result_set.begin (), result_set.end ());
+  std::sort (result.begin (), result.end ());
 
-  /* If there are no eyes, avoid all contact.  I mean, if there are
-     no debug symbols, then add matching minsyms.  But if the user wants
-     to see symbols matching a type m_symbol_namregexp, then never give a minimal symbol,
-     as we assume that a minimal symbol does not have a type.  */
-
-  if ((found_misc || (filenames.empty () && m_kind != FUNCTIONS_DOMAIN))
+  /* If there are no debug symbols, then add matching minsyms.  But if the
+     user wants to see symbols matching a type regexp, then never give a
+     minimal symbol, as we assume that a minimal symbol does not have a
+     type.  */
+  if ((found_misc || (filenames.empty () && m_kind == VARIABLES_DOMAIN))
       && !m_exclude_minsyms
       && !treg.has_value ())
     {
+      gdb_assert (m_kind == VARIABLES_DOMAIN || m_kind == FUNCTIONS_DOMAIN);
       for (objfile *objfile : current_program_space->objfiles ())
-	{
-	  for (minimal_symbol *msymbol : objfile->msymbols ())
-	    {
-	      QUIT;
-
-	      if (msymbol->created_by_gdb)
-		continue;
-
-	      if (MSYMBOL_TYPE (msymbol) == ourtype
-		  || MSYMBOL_TYPE (msymbol) == ourtype2
-		  || MSYMBOL_TYPE (msymbol) == ourtype3
-		  || MSYMBOL_TYPE (msymbol) == ourtype4)
-		{
-		  if (!preg.has_value ()
-		      || preg->exec (MSYMBOL_NATURAL_NAME (msymbol), 0,
-				     NULL, 0) == 0)
-		    {
-		      /* For functions we can do a quick check of whether the
-			 symbol might be found via find_pc_symtab.  */
-		      if (m_kind != FUNCTIONS_DOMAIN
-			  || (find_pc_compunit_symtab
-			      (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
-			      == NULL))
-			{
-			  if (lookup_symbol_in_objfile_from_linkage_name
-			      (objfile, MSYMBOL_LINKAGE_NAME (msymbol),
-			       VAR_DOMAIN)
-			      .symbol == NULL)
-			    {
-			      /* match */
-			      result.emplace_back (i, msymbol, objfile);
-			    }
-			}
-		    }
-		}
-	    }
-	}
+	if (!add_matching_msymbols (objfile, preg, &result))
+	  break;
     }
 
   return result;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index d4dbc2c..aa30f39 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -35,6 +35,8 @@
 #include "completer.h"
 #include "gdb-demangle.h"
 
+#include <set>
+
 /* Opaque declarations.  */
 struct ui_file;
 struct frame_info;
@@ -2106,6 +2108,12 @@
     m_exclude_minsyms = exclude_minsyms;
   }
 
+  /* Set the maximum number of search results to be returned.  */
+  void set_max_search_results (int max_search_results)
+  {
+    m_max_search_results = max_search_results;
+  }
+
   /* Search the symbols from all objfiles in the current program space
      looking for matches as defined by the current state of this object.
 
@@ -2138,6 +2146,41 @@
   /* When this flag is false then minsyms that match M_SYMBOL_REGEXP will
      be included in the results, otherwise they are excluded.  */
   bool m_exclude_minsyms = false;
+
+  /* Maximum number of search results, set to -1 for unlimited, otherwise
+     set to a positive value to limit the number of results returned.  */
+  int m_max_search_results = -1;
+
+  /* Expand symtabs in OBJFILE that match PREG, are of type M_KIND.  Return
+     true if any msymbols were seen that we should later consider adding to
+     the results list.  */
+  bool expand_symtabs (objfile *objfile,
+		       const gdb::optional<compiled_regex> &preg) const;
+
+  /* Add symbols from symtabs in OBJFILE that match PREG, and TREG, and are
+     of type M_KIND, to the results set RESULTS_SET.  Return false if we
+     stop adding results early due to having already found too many results
+     (based on MAX_SEARCH_RESULTS limit), otherwise return true.  Returning
+     true does not indicate that any results were added, just that we
+     didn't _not_ add a result due to reaching MAX_SEARCH_RESULTS.  */
+  bool add_matching_symbols (objfile *objfile,
+			     const gdb::optional<compiled_regex> &preg,
+			     const gdb::optional<compiled_regex> &treg,
+			     std::set<symbol_search> *result_set) const;
+
+  /* Add msymbols from OBJFILE that match PREG and M_KIND, to the
+     results vector RESULTS.  Return false if we stop adding results early
+     due to having already found too many results (based on max search
+     results limit in SEARCH_SPEC), otherwise return true.  Returning true
+     does not indicate that any results were added, just that we didn't
+     _not_ add a result due to reaching MAX_SEARCH_RESULTS.  */
+  bool add_matching_msymbols (objfile *objfile,
+			      const gdb::optional<compiled_regex> &preg,
+			      std::vector<symbol_search> *results) const;
+
+  /* Return true if MSYMBOL is of type KIND.  */
+  static bool is_suitable_msymbol (const enum search_domain kind,
+				   const minimal_symbol *msymbol);
 };
 
 /* When searching for Fortran symbols within modules (functions/variables)
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 518d9cb..6807104 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,9 @@
 2019-11-22  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* gdb.mi/mi-sym-info.exp: Add tests for -max-results parameter.
+
+2019-11-22  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* gdb.mi/mi-fortran-modules.exp: Add additional tests for
 	-symbol-info-module-functions and -symbol-info-module-variables.
 
diff --git a/gdb/testsuite/gdb.mi/mi-sym-info.exp b/gdb/testsuite/gdb.mi/mi-sym-info.exp
index 33fe865..80468a4 100644
--- a/gdb/testsuite/gdb.mi/mi-sym-info.exp
+++ b/gdb/testsuite/gdb.mi/mi-sym-info.exp
@@ -127,3 +127,28 @@
 mi_gdb_test "120-symbol-info-types --name _int_" \
     "120\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]+$srcfile\",fullname=\"\[^\"\]+$srcfile\",symbols=\\\[\{line=\"27\",name=\"my_int_t\"\}\\\]\},\{filename=\"\[^\"\]+$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[\{line=\"23\",name=\"another_int_t\"\}\\\]\}\\\]\}" \
     "List all types matching _int_"
+
+# Test the --max-results parameter.
+mi_gdb_test "121-symbol-info-functions --max-results 0" \
+    "121\\^done,symbols=\{\}" \
+    "-symbol-info-functions --max-results 0"
+
+mi_gdb_test "122-symbol-info-functions --max-results 1" \
+    "122\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]+$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[\{line=\"39\",name=\"f3\",type=\"int \\(another_int_t\\)\",description=\"int f3\\(another_int_t\\);\"\}\\\]\}\\\]\}" \
+    "-symbol-info-functions --max-results 1"
+
+mi_gdb_test "123-symbol-info-functions --max-results 2" \
+    "123\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]+$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[\{line=\"33\",name=\"f2\",type=\"float \\(another_float_t\\)\",description=\"float f2\\(another_float_t\\);\"\},\{line=\"39\",name=\"f3\",type=\"int \\(another_int_t\\)\",description=\"int f3\\(another_int_t\\);\"\}\\\]\}\\\]\}" \
+    "-symbol-info-functions --max-results 2"
+
+mi_gdb_test "124-symbol-info-variables --max-results 3" \
+    "124\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]+$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[\{line=\"21\",name=\"global_f2\",type=\"int\",description=\"int global_f2;\"\},\{line=\"20\",name=\"global_i2\",type=\"int\",description=\"int global_i2;\"\},\{line=\"19\",name=\"global_f1\",type=\"float\",description=\"static float global_f1;\"\}\\\]\}\\\]\}" \
+    "-symbol-info-variables --max-results 3"
+
+mi_gdb_test "125-symbol-info-types --max-results 4" \
+    "125\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]+$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[\{line=\"24\",name=\"another_float_t\"\},\{line=\"23\",name=\"another_int_t\"\},\{name=\"float\"\},\{name=\"int\"\}\\\]\}\\\]\}" \
+    "-symbol-info-types --max-results 4"
+
+
+
+
