diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 615a07089ee..7bcc5e501de 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1256,6 +1256,7 @@ ANALYZER_OBJS = \
 	analyzer/analyzer-selftests.o \
 	analyzer/bar-chart.o \
 	analyzer/bounds-checking.o \
+	analyzer/call-details.o \
 	analyzer/call-info.o \
 	analyzer/call-string.o \
 	analyzer/call-summary.o \
@@ -1268,6 +1269,8 @@ ANALYZER_OBJS = \
 	analyzer/feasible-graph.o \
 	analyzer/function-set.o \
 	analyzer/infinite-recursion.o \
+	analyzer/kf-analyzer.o \
+	analyzer/kf-lang-cp.o \
 	analyzer/known-function-manager.o \
 	analyzer/pending-diagnostic.o \
 	analyzer/program-point.o \
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index a2d363f1249..418d4210755 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -259,8 +259,10 @@ public:
 };
 
 extern void register_known_functions (known_function_manager &mgr);
+extern void register_known_analyzer_functions (known_function_manager &kfm);
 extern void register_known_fd_functions (known_function_manager &kfm);
 extern void register_known_file_functions (known_function_manager &kfm);
+extern void register_known_functions_lang_cp (known_function_manager &kfm);
 extern void register_varargs_builtins (known_function_manager &kfm);
 
 /* Passed by pointer to PLUGIN_ANALYZER_INIT callbacks.  */
diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc
new file mode 100644
index 00000000000..b63b8a38d88
--- /dev/null
+++ b/gcc/analyzer/call-details.cc
@@ -0,0 +1,231 @@
+/* Helper class for handling a call with specific arguments.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "diagnostic.h"
+#include "tree-diagnostic.h" /* for default_tree_printer.  */
+#include "gimple-pretty-print.h"
+#include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* class call_details.  */
+
+/* call_details's ctor.  */
+
+call_details::call_details (const gcall *call, region_model *model,
+			    region_model_context *ctxt)
+: m_call (call), m_model (model), m_ctxt (ctxt),
+  m_lhs_type (NULL_TREE), m_lhs_region (NULL)
+{
+  m_lhs_type = NULL_TREE;
+  if (tree lhs = gimple_call_lhs (call))
+    {
+      m_lhs_region = model->get_lvalue (lhs, ctxt);
+      m_lhs_type = TREE_TYPE (lhs);
+    }
+}
+
+/* Get the manager from m_model.  */
+
+region_model_manager *
+call_details::get_manager () const
+{
+  return m_model->get_manager ();
+}
+
+/* Get any logger associated with this object.  */
+
+logger *
+call_details::get_logger () const
+{
+  if (m_ctxt)
+    return m_ctxt->get_logger ();
+  else
+    return NULL;
+}
+
+/* Get any uncertainty_t associated with the region_model_context.  */
+
+uncertainty_t *
+call_details::get_uncertainty () const
+{
+  if (m_ctxt)
+    return m_ctxt->get_uncertainty ();
+  else
+    return NULL;
+}
+
+/* If the callsite has a left-hand-side region, set it to RESULT
+   and return true.
+   Otherwise do nothing and return false.  */
+
+bool
+call_details::maybe_set_lhs (const svalue *result) const
+{
+  gcc_assert (result);
+  if (m_lhs_region)
+    {
+      m_model->set_value (m_lhs_region, result, m_ctxt);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Return the number of arguments used by the call statement.  */
+
+unsigned
+call_details::num_args () const
+{
+  return gimple_call_num_args (m_call);
+}
+
+/* Return true if argument IDX is a size_t (or compatible with it).  */
+
+bool
+call_details::arg_is_size_p (unsigned idx) const
+{
+  return types_compatible_p (get_arg_type (idx), size_type_node);
+}
+
+/* Get the location of the call statement.  */
+
+location_t
+call_details::get_location () const
+{
+  return m_call->location;
+}
+
+/* Get argument IDX at the callsite as a tree.  */
+
+tree
+call_details::get_arg_tree (unsigned idx) const
+{
+  return gimple_call_arg (m_call, idx);
+}
+
+/* Get the type of argument IDX.  */
+
+tree
+call_details::get_arg_type (unsigned idx) const
+{
+  return TREE_TYPE (gimple_call_arg (m_call, idx));
+}
+
+/* Get argument IDX at the callsite as an svalue.  */
+
+const svalue *
+call_details::get_arg_svalue (unsigned idx) const
+{
+  tree arg = get_arg_tree (idx);
+  return m_model->get_rvalue (arg, m_ctxt);
+}
+
+/* Attempt to get the string literal for argument IDX, or return NULL
+   otherwise.
+   For use when implementing "__analyzer_*" functions that take
+   string literals.  */
+
+const char *
+call_details::get_arg_string_literal (unsigned idx) const
+{
+  const svalue *str_arg = get_arg_svalue (idx);
+  if (const region *pointee = str_arg->maybe_get_region ())
+    if (const string_region *string_reg = pointee->dyn_cast_string_region ())
+      {
+	tree string_cst = string_reg->get_string_cst ();
+	return TREE_STRING_POINTER (string_cst);
+      }
+  return NULL;
+}
+
+/* Attempt to get the fndecl used at this call, if known, or NULL_TREE
+   otherwise.  */
+
+tree
+call_details::get_fndecl_for_call () const
+{
+  return m_model->get_fndecl_for_call (m_call, m_ctxt);
+}
+
+/* Dump a multiline representation of this call to PP.  */
+
+void
+call_details::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  pp_string (pp, "gcall: ");
+  pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
+  pp_newline (pp);
+  pp_string (pp, "return region: ");
+  if (m_lhs_region)
+    m_lhs_region->dump_to_pp (pp, simple);
+  else
+    pp_string (pp, "NULL");
+  pp_newline (pp);
+  for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
+    {
+      const svalue *arg_sval = get_arg_svalue (i);
+      pp_printf (pp, "arg %i: ", i);
+      arg_sval->dump_to_pp (pp, simple);
+      pp_newline (pp);
+    }
+}
+
+/* Dump a multiline representation of this call to stderr.  */
+
+DEBUG_FUNCTION void
+call_details::dump (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp, simple);
+  pp_flush (&pp);
+}
+
+/* Get a conjured_svalue for this call for REG,
+   and purge any state already relating to that conjured_svalue.  */
+
+const svalue *
+call_details::get_or_create_conjured_svalue (const region *reg) const
+{
+  region_model_manager *mgr = m_model->get_manager ();
+  return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
+					     conjured_purge (m_model, m_ctxt));
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/call-details.h b/gcc/analyzer/call-details.h
new file mode 100644
index 00000000000..144ca0ce868
--- /dev/null
+++ b/gcc/analyzer/call-details.h
@@ -0,0 +1,77 @@
+/* Helper class for handling a call with specific arguments.
+   Copyright (C) 2019-2022 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_CALL_DETAILS_H
+#define GCC_ANALYZER_CALL_DETAILS_H
+
+namespace ana {
+
+/* Helper class for handling calls to functions with known behavior.  */
+
+class call_details
+{
+public:
+  call_details (const gcall *call, region_model *model,
+		region_model_context *ctxt);
+
+  region_model *get_model () const { return m_model; }
+  region_model_manager *get_manager () const;
+  region_model_context *get_ctxt () const { return m_ctxt; }
+  logger *get_logger () const;
+
+  uncertainty_t *get_uncertainty () const;
+  tree get_lhs_type () const { return m_lhs_type; }
+  const region *get_lhs_region () const { return m_lhs_region; }
+
+  bool maybe_set_lhs (const svalue *result) const;
+
+  unsigned num_args () const;
+  bool arg_is_pointer_p (unsigned idx) const
+  {
+    return POINTER_TYPE_P (get_arg_type (idx));
+  }
+  bool arg_is_size_p (unsigned idx) const;
+
+  const gcall *get_call_stmt () const { return m_call; }
+  location_t get_location () const;
+
+  tree get_arg_tree (unsigned idx) const;
+  tree get_arg_type (unsigned idx) const;
+  const svalue *get_arg_svalue (unsigned idx) const;
+  const char *get_arg_string_literal (unsigned idx) const;
+
+  tree get_fndecl_for_call () const;
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const;
+  void dump (bool simple) const;
+
+  const svalue *get_or_create_conjured_svalue (const region *) const;
+
+private:
+  const gcall *m_call;
+  region_model *m_model;
+  region_model_context *m_ctxt;
+  tree m_lhs_type;
+  const region *m_lhs_region;
+};
+
+} // namespace ana
+
+#endif /* GCC_ANALYZER_CALL_DETAILS_H */
diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc
index 44a66be075a..cbd21f7472b 100644
--- a/gcc/analyzer/call-info.cc
+++ b/gcc/analyzer/call-info.cc
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/checker-path.h"
 #include "analyzer/diagnostic-manager.h"
 #include "analyzer/exploded-graph.h"
+#include "analyzer/call-details.h"
 #include "analyzer/call-info.h"
 #include "make-unique.h"
 
diff --git a/gcc/analyzer/call-summary.h b/gcc/analyzer/call-summary.h
index 07cd3f53ce6..73f21ac7282 100644
--- a/gcc/analyzer/call-summary.h
+++ b/gcc/analyzer/call-summary.h
@@ -20,6 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_CALL_SUMMARY_H
 #define GCC_ANALYZER_CALL_SUMMARY_H
 
+#include "call-details.h"
+
 namespace ana {
 
 /* A class summarizing one particular outcome of a function that
diff --git a/gcc/analyzer/kf-analyzer.cc b/gcc/analyzer/kf-analyzer.cc
new file mode 100644
index 00000000000..b233418beb7
--- /dev/null
+++ b/gcc/analyzer/kf-analyzer.cc
@@ -0,0 +1,386 @@
+/* Handling for the various __analyzer_* known functions.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "diagnostic.h"
+#include "tree-diagnostic.h" /* for default_tree_printer.  */
+#include "analyzer/region-model.h"
+#include "analyzer/pending-diagnostic.h"
+#include "analyzer/call-details.h"
+#include "make-unique.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* Handle calls to "__analyzer_break" by triggering a breakpoint within
+   the analyzer.  */
+
+class kf_analyzer_break : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 0;
+  }
+  void impl_call_pre (const call_details &) const final override
+  {
+    /* TODO: is there a good cross-platform way to do this?  */
+    raise (SIGINT);
+  }
+};
+
+/* Handler for calls to "__analyzer_describe".
+
+   Emit a warning describing the 2nd argument (which can be of any
+   type), at the given verbosity level.  This is for use when
+   debugging, and may be of use in DejaGnu tests.  */
+
+class kf_analyzer_describe : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 2;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    if (!cd.get_ctxt ())
+      return;
+    tree t_verbosity = cd.get_arg_tree (0);
+    const svalue *sval = cd.get_arg_svalue (1);
+    bool simple = zerop (t_verbosity);
+    label_text desc = sval->get_desc (simple);
+    warning_at (cd.get_location (), 0, "svalue: %qs", desc.get ());
+  }
+};
+
+/* Handler for calls to "__analyzer_dump_capacity".
+
+   Emit a warning describing the capacity of the base region of
+   the region pointed to by the 1st argument.
+   This is for use when debugging, and may be of use in DejaGnu tests.  */
+
+class kf_analyzer_dump_capacity : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return (cd.num_args () == 1
+	    && cd.arg_is_pointer_p (0));
+  }
+
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_context *ctxt = cd.get_ctxt ();
+    if (!ctxt)
+      return;
+    region_model *model = cd.get_model ();
+    tree t_ptr = cd.get_arg_tree (0);
+    const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt);
+    const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt);
+    const region *base_reg = reg->get_base_region ();
+    const svalue *capacity = model->get_capacity (base_reg);
+    label_text desc = capacity->get_desc (true);
+    warning_at (cd.get_call_stmt ()->location, 0,
+		"capacity: %qs", desc.get ());
+  }
+};
+
+/* Compare D1 and D2 using their names, and then IDs to order them.  */
+
+static int
+cmp_decls (tree d1, tree d2)
+{
+  gcc_assert (DECL_P (d1));
+  gcc_assert (DECL_P (d2));
+  if (DECL_NAME (d1) && DECL_NAME (d2))
+    if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)),
+			  IDENTIFIER_POINTER (DECL_NAME (d2))))
+      return cmp;
+  return (int)DECL_UID (d1) - (int)DECL_UID (d2);
+}
+
+/* Comparator for use by vec<tree>::qsort,
+   using their names, and then IDs to order them.  */
+
+static int
+cmp_decls_ptr_ptr (const void *p1, const void *p2)
+{
+  tree const *d1 = (tree const *)p1;
+  tree const *d2 = (tree const *)p2;
+
+  return cmp_decls (*d1, *d2);
+}
+
+/* Handler for calls to "__analyzer_dump_escaped".
+
+   Emit a warning giving the number of decls that have escaped, followed
+   by a comma-separated list of their names, in alphabetical order.
+
+   This is for use when debugging, and may be of use in DejaGnu tests.  */
+
+class kf_analyzer_dump_escaped : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 0;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_context *ctxt = cd.get_ctxt ();
+    if (!ctxt)
+      return;
+    region_model *model = cd.get_model ();
+
+    auto_vec<tree> escaped_decls;
+    for (auto iter : *model->get_store ())
+      {
+	const binding_cluster *c = iter.second;
+	if (!c->escaped_p ())
+	  continue;
+	if (tree decl = c->get_base_region ()->maybe_get_decl ())
+	  escaped_decls.safe_push (decl);
+      }
+
+    /* Sort them into deterministic order; alphabetical is
+       probably most user-friendly.  */
+    escaped_decls.qsort (cmp_decls_ptr_ptr);
+
+    pretty_printer pp;
+    pp_format_decoder (&pp) = default_tree_printer;
+    pp_show_color (&pp) = pp_show_color (global_dc->printer);
+    bool first = true;
+    for (auto iter : escaped_decls)
+      {
+	if (first)
+	  first = false;
+	else
+	  pp_string (&pp, ", ");
+	pp_printf (&pp, "%qD", iter);
+      }
+    /* Print the number to make it easier to write DejaGnu tests for
+       the "nothing has escaped" case.  */
+    warning_at (cd.get_location (), 0, "escaped: %i: %s",
+		escaped_decls.length (),
+		pp_formatted_text (&pp));
+  }
+};
+
+/* Placeholder handler for calls to "__analyzer_dump_exploded_nodes".
+   This is a no-op; the real implementation happens when the
+   exploded_graph is postprocessed.  */
+
+class kf_analyzer_dump_exploded_nodes : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 1;
+  }
+};
+
+/* Handler for calls to "__analyzer_dump_named_constant".
+
+   Look up the given name, and emit a warning describing the
+   state of the corresponding stashed value.
+
+   This is for use when debugging, and for DejaGnu tests.  */
+
+class kf_analyzer_dump_named_constant : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 1;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_context *ctxt = cd.get_ctxt ();
+    if (!ctxt)
+      return;
+
+    const char *name = cd.get_arg_string_literal (0);
+    if (!name)
+      {
+	error_at (cd.get_location (), "cannot determine name");
+	return;
+      }
+    tree value = get_stashed_constant_by_name (name);
+    if (value)
+      warning_at (cd.get_location (), 0, "named constant %qs has value %qE",
+		  name, value);
+    else
+      warning_at (cd.get_location (), 0, "named constant %qs has unknown value",
+		  name);
+  }
+};
+
+/* A pending_diagnostic subclass for implementing "__analyzer_dump_path".  */
+
+class dump_path_diagnostic
+  : public pending_diagnostic_subclass<dump_path_diagnostic>
+{
+public:
+  int get_controlling_option () const final override
+  {
+    return 0;
+  }
+
+  bool emit (rich_location *richloc) final override
+  {
+    inform (richloc, "path");
+    return true;
+  }
+
+  const char *get_kind () const final override
+  {
+    return "dump_path_diagnostic";
+  }
+
+  bool operator== (const dump_path_diagnostic &) const
+  {
+    return true;
+  }
+};
+
+/* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this
+   exploded_node.  */
+
+class kf_analyzer_dump_path : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 0;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_context *ctxt = cd.get_ctxt ();
+    if (!ctxt)
+      return;
+    ctxt->warn (make_unique<dump_path_diagnostic> ());
+  }
+};
+
+/* Handle calls to "__analyzer_dump_region_model" by dumping
+   the region model's state to stderr.  */
+
+class kf_analyzer_dump_region_model : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 0;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_context *ctxt = cd.get_ctxt ();
+    if (!ctxt)
+      return;
+    region_model *model = cd.get_model ();
+    model->dump (false);
+  }
+};
+
+/* Handle a call to "__analyzer_eval" by evaluating the input
+   and dumping as a dummy warning, so that test cases can use
+   dg-warning to validate the result (and so unexpected warnings will
+   lead to DejaGnu failures).
+   Broken out as a subroutine to make it easier to put a breakpoint on it
+   - though typically this doesn't help, as we have an SSA name as the arg,
+   and what's more interesting is usually the def stmt for that name.  */
+
+class kf_analyzer_eval : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 1;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_context *ctxt = cd.get_ctxt ();
+    if (!ctxt)
+      return;
+    region_model *model = cd.get_model ();
+
+    tree t_arg = cd.get_arg_tree (0);
+    tristate t = model->eval_condition (t_arg, NE_EXPR, integer_zero_node,
+					ctxt);
+    warning_at (cd.get_location (), 0, "%s", t.as_string ());
+  }
+};
+
+/* Handler for "__analyzer_get_unknown_ptr".  */
+
+class kf_analyzer_get_unknown_ptr : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 0;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model_manager *mgr = cd.get_manager ();
+    const svalue *ptr_sval
+      = mgr->get_or_create_unknown_svalue (cd.get_lhs_type ());
+    cd.maybe_set_lhs (ptr_sval);
+  }
+};
+
+/* Populate KFM with instances of known functions used for debugging the
+   analyzer and for writing DejaGnu tests, all with a "__analyzer_" prefix.  */
+
+void
+register_known_analyzer_functions (known_function_manager &kfm)
+{
+  kfm.add ("__analyzer_break", make_unique<kf_analyzer_break> ());
+  kfm.add ("__analyzer_describe", make_unique<kf_analyzer_describe> ());
+  kfm.add ("__analyzer_dump_capacity",
+	   make_unique<kf_analyzer_dump_capacity> ());
+  kfm.add ("__analyzer_dump_escaped", make_unique<kf_analyzer_dump_escaped> ());
+  kfm.add ("__analyzer_dump_exploded_nodes",
+	   make_unique<kf_analyzer_dump_exploded_nodes> ());
+  kfm.add ("__analyzer_dump_named_constant",
+	   make_unique<kf_analyzer_dump_named_constant> ());
+  kfm.add ("__analyzer_dump_path", make_unique<kf_analyzer_dump_path> ());
+  kfm.add ("__analyzer_dump_region_model",
+	   make_unique<kf_analyzer_dump_region_model> ());
+  kfm.add ("__analyzer_eval", make_unique<kf_analyzer_eval> ());
+  kfm.add ("__analyzer_get_unknown_ptr",
+	   make_unique<kf_analyzer_get_unknown_ptr> ());
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/kf-lang-cp.cc b/gcc/analyzer/kf-lang-cp.cc
new file mode 100644
index 00000000000..9dca3669994
--- /dev/null
+++ b/gcc/analyzer/kf-lang-cp.cc
@@ -0,0 +1,111 @@
+/* Handling for the known behavior of various functions specific to C++.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "diagnostic.h"
+#include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
+#include "make-unique.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* Implementations of specific functions.  */
+
+/* Handler for "operator new" and "operator new []".  */
+
+class kf_operator_new : public known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == 1;
+  }
+
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    region_model *model = cd.get_model ();
+    region_model_manager *mgr = cd.get_manager ();
+    const svalue *size_sval = cd.get_arg_svalue (0);
+    const region *new_reg
+      = model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
+    if (cd.get_lhs_type ())
+      {
+	const svalue *ptr_sval
+	  = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+	cd.maybe_set_lhs (ptr_sval);
+      }
+  }
+};
+
+/* Handler for "operator delete", both the sized and unsized variants
+   (2 arguments and 1 argument respectively), and for "operator delete []"  */
+
+class kf_operator_delete : public known_function
+{
+public:
+  kf_operator_delete (unsigned num_args) : m_num_args (num_args) {}
+
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return cd.num_args () == m_num_args;
+  }
+
+  void impl_call_post (const call_details &cd) const final override
+  {
+    region_model *model = cd.get_model ();
+    const svalue *ptr_sval = cd.get_arg_svalue (0);
+    if (const region *freed_reg = ptr_sval->maybe_get_region ())
+      {
+	/* If the ptr points to an underlying heap region, delete it,
+	   poisoning pointers.  */
+	model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
+      }
+  }
+
+private:
+  unsigned m_num_args;
+};
+
+/* Populate KFM with instances of known functions relating to C++.  */
+
+void
+register_known_functions_lang_cp (known_function_manager &kfm)
+{
+  kfm.add ("operator new", make_unique<kf_operator_new> ());
+  kfm.add ("operator new []", make_unique<kf_operator_new> ());
+  kfm.add ("operator delete", make_unique<kf_operator_delete> (1));
+  kfm.add ("operator delete", make_unique<kf_operator_delete> (2));
+  kfm.add ("operator delete []", make_unique<kf_operator_delete> (1));
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc
index c1074bcb6e5..dc514a7fe57 100644
--- a/gcc/analyzer/known-function-manager.cc
+++ b/gcc/analyzer/known-function-manager.cc
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 #include "analyzer/known-function-manager.h"
 #include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
 
 #if ENABLE_ANALYZER
 
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 8ba644c33cd..6aeb9281bff 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -26,228 +26,20 @@ along with GCC; see the file COPYING3.  If not see
 #include "function.h"
 #include "basic-block.h"
 #include "gimple.h"
-#include "gimple-iterator.h"
 #include "diagnostic-core.h"
-#include "graphviz.h"
-#include "options.h"
-#include "cgraph.h"
-#include "tree-dfa.h"
-#include "stringpool.h"
-#include "convert.h"
-#include "target.h"
-#include "fold-const.h"
-#include "tree-pretty-print.h"
-#include "diagnostic-color.h"
 #include "diagnostic-metadata.h"
-#include "bitmap.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
-#include "ordered-hash-map.h"
-#include "options.h"
-#include "analyzer/supergraph.h"
-#include "sbitmap.h"
-#include "analyzer/call-string.h"
-#include "analyzer/program-point.h"
-#include "analyzer/store.h"
+#include "diagnostic.h"
 #include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
 #include "analyzer/call-info.h"
-#include "analyzer/sm.h"
-#include "diagnostic-path.h"
-#include "analyzer/pending-diagnostic.h"
-#include "gimple-pretty-print.h"
 #include "make-unique.h"
 
 #if ENABLE_ANALYZER
 
 namespace ana {
 
-/* class call_details.  */
-
-/* call_details's ctor.  */
-
-call_details::call_details (const gcall *call, region_model *model,
-			    region_model_context *ctxt)
-: m_call (call), m_model (model), m_ctxt (ctxt),
-  m_lhs_type (NULL_TREE), m_lhs_region (NULL)
-{
-  m_lhs_type = NULL_TREE;
-  if (tree lhs = gimple_call_lhs (call))
-    {
-      m_lhs_region = model->get_lvalue (lhs, ctxt);
-      m_lhs_type = TREE_TYPE (lhs);
-    }
-}
-
-/* Get the manager from m_model.  */
-
-region_model_manager *
-call_details::get_manager () const
-{
-  return m_model->get_manager ();
-}
-
-/* Get any logger associated with this object.  */
-
-logger *
-call_details::get_logger () const
-{
-  if (m_ctxt)
-    return m_ctxt->get_logger ();
-  else
-    return NULL;
-}
-
-/* Get any uncertainty_t associated with the region_model_context.  */
-
-uncertainty_t *
-call_details::get_uncertainty () const
-{
-  if (m_ctxt)
-    return m_ctxt->get_uncertainty ();
-  else
-    return NULL;
-}
-
-/* If the callsite has a left-hand-side region, set it to RESULT
-   and return true.
-   Otherwise do nothing and return false.  */
-
-bool
-call_details::maybe_set_lhs (const svalue *result) const
-{
-  gcc_assert (result);
-  if (m_lhs_region)
-    {
-      m_model->set_value (m_lhs_region, result, m_ctxt);
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Return the number of arguments used by the call statement.  */
-
-unsigned
-call_details::num_args () const
-{
-  return gimple_call_num_args (m_call);
-}
-
-/* Return true if argument IDX is a size_t (or compatible with it).  */
-
-bool
-call_details::arg_is_size_p (unsigned idx) const
-{
-  return types_compatible_p (get_arg_type (idx), size_type_node);
-}
-
-/* Get the location of the call statement.  */
-
-location_t
-call_details::get_location () const
-{
-  return m_call->location;
-}
-
-/* Get argument IDX at the callsite as a tree.  */
-
-tree
-call_details::get_arg_tree (unsigned idx) const
-{
-  return gimple_call_arg (m_call, idx);
-}
-
-/* Get the type of argument IDX.  */
-
-tree
-call_details::get_arg_type (unsigned idx) const
-{
-  return TREE_TYPE (gimple_call_arg (m_call, idx));
-}
-
-/* Get argument IDX at the callsite as an svalue.  */
-
-const svalue *
-call_details::get_arg_svalue (unsigned idx) const
-{
-  tree arg = get_arg_tree (idx);
-  return m_model->get_rvalue (arg, m_ctxt);
-}
-
-/* Attempt to get the string literal for argument IDX, or return NULL
-   otherwise.
-   For use when implementing "__analyzer_*" functions that take
-   string literals.  */
-
-const char *
-call_details::get_arg_string_literal (unsigned idx) const
-{
-  const svalue *str_arg = get_arg_svalue (idx);
-  if (const region *pointee = str_arg->maybe_get_region ())
-    if (const string_region *string_reg = pointee->dyn_cast_string_region ())
-      {
-	tree string_cst = string_reg->get_string_cst ();
-	return TREE_STRING_POINTER (string_cst);
-      }
-  return NULL;
-}
-
-/* Attempt to get the fndecl used at this call, if known, or NULL_TREE
-   otherwise.  */
-
-tree
-call_details::get_fndecl_for_call () const
-{
-  return m_model->get_fndecl_for_call (m_call, m_ctxt);
-}
-
-/* Dump a multiline representation of this call to PP.  */
-
-void
-call_details::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  pp_string (pp, "gcall: ");
-  pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
-  pp_newline (pp);
-  pp_string (pp, "return region: ");
-  if (m_lhs_region)
-    m_lhs_region->dump_to_pp (pp, simple);
-  else
-    pp_string (pp, "NULL");
-  pp_newline (pp);
-  for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
-    {
-      const svalue *arg_sval = get_arg_svalue (i);
-      pp_printf (pp, "arg %i: ", i);
-      arg_sval->dump_to_pp (pp, simple);
-      pp_newline (pp);
-    }
-}
-
-/* Dump a multiline representation of this call to stderr.  */
-
-DEBUG_FUNCTION void
-call_details::dump (bool simple) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  pp_show_color (&pp) = pp_show_color (global_dc->printer);
-  pp.buffer->stream = stderr;
-  dump_to_pp (&pp, simple);
-  pp_flush (&pp);
-}
-
-/* Get a conjured_svalue for this call for REG,
-   and purge any state already relating to that conjured_svalue.  */
-
-const svalue *
-call_details::get_or_create_conjured_svalue (const region *reg) const
-{
-  region_model_manager *mgr = m_model->get_manager ();
-  return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
-					     conjured_purge (m_model, m_ctxt));
-}
-
 /* Implementations of specific functions.  */
 
 /* Handler for "alloca".  */
@@ -277,324 +69,6 @@ kf_alloca::impl_call_pre (const call_details &cd) const
   cd.maybe_set_lhs (ptr_sval);
 }
 
-/* Handle calls to "__analyzer_break" by triggering a breakpoint within
-   the analyzer.  */
-
-class kf_analyzer_break : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 0;
-  }
-  void impl_call_pre (const call_details &) const final override
-  {
-    /* TODO: is there a good cross-platform way to do this?  */
-    raise (SIGINT);
-  }
-};
-
-/* Handler for calls to "__analyzer_describe".
-
-   Emit a warning describing the 2nd argument (which can be of any
-   type), at the given verbosity level.  This is for use when
-   debugging, and may be of use in DejaGnu tests.  */
-
-class kf_analyzer_describe : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 2;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    if (!cd.get_ctxt ())
-      return;
-    tree t_verbosity = cd.get_arg_tree (0);
-    const svalue *sval = cd.get_arg_svalue (1);
-    bool simple = zerop (t_verbosity);
-    label_text desc = sval->get_desc (simple);
-    warning_at (cd.get_location (), 0, "svalue: %qs", desc.get ());
-  }
-};
-
-/* Handler for calls to "__analyzer_dump_capacity".
-
-   Emit a warning describing the capacity of the base region of
-   the region pointed to by the 1st argument.
-   This is for use when debugging, and may be of use in DejaGnu tests.  */
-
-class kf_analyzer_dump_capacity : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return (cd.num_args () == 1
-	    && cd.arg_is_pointer_p (0));
-  }
-
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_context *ctxt = cd.get_ctxt ();
-    if (!ctxt)
-      return;
-    region_model *model = cd.get_model ();
-    tree t_ptr = cd.get_arg_tree (0);
-    const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt);
-    const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt);
-    const region *base_reg = reg->get_base_region ();
-    const svalue *capacity = model->get_capacity (base_reg);
-    label_text desc = capacity->get_desc (true);
-    warning_at (cd.get_call_stmt ()->location, 0,
-		"capacity: %qs", desc.get ());
-  }
-};
-
-/* Compare D1 and D2 using their names, and then IDs to order them.  */
-
-static int
-cmp_decls (tree d1, tree d2)
-{
-  gcc_assert (DECL_P (d1));
-  gcc_assert (DECL_P (d2));
-  if (DECL_NAME (d1) && DECL_NAME (d2))
-    if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)),
-			  IDENTIFIER_POINTER (DECL_NAME (d2))))
-      return cmp;
-  return (int)DECL_UID (d1) - (int)DECL_UID (d2);
-}
-
-/* Comparator for use by vec<tree>::qsort,
-   using their names, and then IDs to order them.  */
-
-static int
-cmp_decls_ptr_ptr (const void *p1, const void *p2)
-{
-  tree const *d1 = (tree const *)p1;
-  tree const *d2 = (tree const *)p2;
-
-  return cmp_decls (*d1, *d2);
-}
-
-/* Handler for calls to "__analyzer_dump_escaped".
-
-   Emit a warning giving the number of decls that have escaped, followed
-   by a comma-separated list of their names, in alphabetical order.
-
-   This is for use when debugging, and may be of use in DejaGnu tests.  */
-
-class kf_analyzer_dump_escaped : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 0;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_context *ctxt = cd.get_ctxt ();
-    if (!ctxt)
-      return;
-    region_model *model = cd.get_model ();
-
-    auto_vec<tree> escaped_decls;
-    for (auto iter : *model->get_store ())
-      {
-	const binding_cluster *c = iter.second;
-	if (!c->escaped_p ())
-	  continue;
-	if (tree decl = c->get_base_region ()->maybe_get_decl ())
-	  escaped_decls.safe_push (decl);
-      }
-
-    /* Sort them into deterministic order; alphabetical is
-       probably most user-friendly.  */
-    escaped_decls.qsort (cmp_decls_ptr_ptr);
-
-    pretty_printer pp;
-    pp_format_decoder (&pp) = default_tree_printer;
-    pp_show_color (&pp) = pp_show_color (global_dc->printer);
-    bool first = true;
-    for (auto iter : escaped_decls)
-      {
-	if (first)
-	  first = false;
-	else
-	  pp_string (&pp, ", ");
-	pp_printf (&pp, "%qD", iter);
-      }
-    /* Print the number to make it easier to write DejaGnu tests for
-       the "nothing has escaped" case.  */
-    warning_at (cd.get_location (), 0, "escaped: %i: %s",
-		escaped_decls.length (),
-		pp_formatted_text (&pp));
-  }
-};
-
-/* Placeholder handler for calls to "__analyzer_dump_exploded_nodes".
-   This is a no-op; the real implementation happens when the
-   exploded_graph is postprocessed.  */
-
-class kf_analyzer_dump_exploded_nodes : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 1;
-  }
-};
-
-/* Handler for calls to "__analyzer_dump_named_constant".
-
-   Look up the given name, and emit a warning describing the
-   state of the corresponding stashed value.
-
-   This is for use when debugging, and for DejaGnu tests.  */
-
-class kf_analyzer_dump_named_constant : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 1;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_context *ctxt = cd.get_ctxt ();
-    if (!ctxt)
-      return;
-
-    const char *name = cd.get_arg_string_literal (0);
-    if (!name)
-      {
-	error_at (cd.get_location (), "cannot determine name");
-	return;
-      }
-    tree value = get_stashed_constant_by_name (name);
-    if (value)
-      warning_at (cd.get_location (), 0, "named constant %qs has value %qE",
-		  name, value);
-    else
-      warning_at (cd.get_location (), 0, "named constant %qs has unknown value",
-		  name);
-  }
-};
-
-/* A pending_diagnostic subclass for implementing "__analyzer_dump_path".  */
-
-class dump_path_diagnostic
-  : public pending_diagnostic_subclass<dump_path_diagnostic>
-{
-public:
-  int get_controlling_option () const final override
-  {
-    return 0;
-  }
-
-  bool emit (rich_location *richloc) final override
-  {
-    inform (richloc, "path");
-    return true;
-  }
-
-  const char *get_kind () const final override
-  {
-    return "dump_path_diagnostic";
-  }
-
-  bool operator== (const dump_path_diagnostic &) const
-  {
-    return true;
-  }
-};
-
-/* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this
-   exploded_node.  */
-
-class kf_analyzer_dump_path : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 0;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_context *ctxt = cd.get_ctxt ();
-    if (!ctxt)
-      return;
-    ctxt->warn (make_unique<dump_path_diagnostic> ());
-  }
-};
-
-/* Handle calls to "__analyzer_dump_region_model" by dumping
-   the region model's state to stderr.  */
-
-class kf_analyzer_dump_region_model : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 0;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_context *ctxt = cd.get_ctxt ();
-    if (!ctxt)
-      return;
-    region_model *model = cd.get_model ();
-    model->dump (false);
-  }
-};
-
-/* Handle a call to "__analyzer_eval" by evaluating the input
-   and dumping as a dummy warning, so that test cases can use
-   dg-warning to validate the result (and so unexpected warnings will
-   lead to DejaGnu failures).
-   Broken out as a subroutine to make it easier to put a breakpoint on it
-   - though typically this doesn't help, as we have an SSA name as the arg,
-   and what's more interesting is usually the def stmt for that name.  */
-
-class kf_analyzer_eval : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 1;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_context *ctxt = cd.get_ctxt ();
-    if (!ctxt)
-      return;
-    region_model *model = cd.get_model ();
-
-    tree t_arg = cd.get_arg_tree (0);
-    tristate t = model->eval_condition (t_arg, NE_EXPR, integer_zero_node,
-					ctxt);
-    warning_at (cd.get_location (), 0, "%s", t.as_string ());
-  }
-};
-
-/* Handler for "__analyzer_get_unknown_ptr".  */
-
-class kf_analyzer_get_unknown_ptr : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 0;
-  }
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model_manager *mgr = cd.get_manager ();
-    const svalue *ptr_sval
-      = mgr->get_or_create_unknown_svalue (cd.get_lhs_type ());
-    cd.maybe_set_lhs (ptr_sval);
-  }
-};
-
 /* Handler for "__builtin_expect" etc.  */
 
 class kf_expect : public internal_known_function
@@ -977,61 +451,6 @@ public:
   }
 };
 
-/* Handler for "operator new" and "operator new []".  */
-
-class kf_operator_new : public known_function
-{
-public:
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == 1;
-  }
-
-  void impl_call_pre (const call_details &cd) const final override
-  {
-    region_model *model = cd.get_model ();
-    region_model_manager *mgr = cd.get_manager ();
-    const svalue *size_sval = cd.get_arg_svalue (0);
-    const region *new_reg
-      = model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
-    if (cd.get_lhs_type ())
-      {
-	const svalue *ptr_sval
-	  = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
-	cd.maybe_set_lhs (ptr_sval);
-      }
-  }
-};
-
-/* Handler for "operator delete", both the sized and unsized variants
-   (2 arguments and 1 argument respectively), and for "operator delete []"  */
-
-class kf_operator_delete : public known_function
-{
-public:
-  kf_operator_delete (unsigned num_args) : m_num_args (num_args) {}
-
-  bool matches_call_types_p (const call_details &cd) const final override
-  {
-    return cd.num_args () == m_num_args;
-  }
-
-  void impl_call_post (const call_details &cd) const final override
-  {
-    region_model *model = cd.get_model ();
-    const svalue *ptr_sval = cd.get_arg_svalue (0);
-    if (const region *freed_reg = ptr_sval->maybe_get_region ())
-      {
-	/* If the ptr points to an underlying heap region, delete it,
-	   poisoning pointers.  */
-	model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
-      }
-  }
-
-private:
-  unsigned m_num_args;
-};
-
 /* Handler for "realloc":
 
      void *realloc(void *ptr, size_t size);
@@ -1490,6 +909,9 @@ region_model::impl_deallocation_call (const call_details &cd)
 void
 register_known_functions (known_function_manager &kfm)
 {
+  /* Debugging/test support functions, all  with a "__analyzer_" prefix.  */
+  register_known_analyzer_functions (kfm);
+
   /* Internal fns the analyzer has known_functions for.  */
   {
     kfm.add (IFN_BUILTIN_EXPECT, make_unique<kf_expect> ());
@@ -1520,27 +942,6 @@ register_known_functions (known_function_manager &kfm)
     register_varargs_builtins (kfm);
   }
 
-  /* Debugging/test support functions, all  with a "__analyzer_" prefix.  */
-  {
-    kfm.add ("__analyzer_break", make_unique<kf_analyzer_break> ());
-    kfm.add ("__analyzer_describe", make_unique<kf_analyzer_describe> ());
-    kfm.add ("__analyzer_dump_capacity",
-	     make_unique<kf_analyzer_dump_capacity> ());
-    kfm.add ("__analyzer_dump_escaped",
-	     make_unique<kf_analyzer_dump_escaped> ());
-    kfm.add ("__analyzer_dump_exploded_nodes",
-	     make_unique<kf_analyzer_dump_exploded_nodes> ());
-    kfm.add ("__analyzer_dump_named_constant",
-	     make_unique<kf_analyzer_dump_named_constant> ());
-    kfm.add ("__analyzer_dump_path", make_unique<kf_analyzer_dump_path> ());
-    kfm.add ("__analyzer_dump_region_model",
-	     make_unique<kf_analyzer_dump_region_model> ());
-    kfm.add ("__analyzer_eval",
-	     make_unique<kf_analyzer_eval> ());
-    kfm.add ("__analyzer_get_unknown_ptr",
-	     make_unique<kf_analyzer_get_unknown_ptr> ());
-  }
-
   /* Known builtins and C standard library functions.  */
   {
     kfm.add ("memset", make_unique<kf_memset> ());
@@ -1575,14 +976,8 @@ register_known_functions (known_function_manager &kfm)
     kfm.add ("__error", make_unique<kf_errno_location> ());
   }
 
-  /* C++ support functions.  */
-  {
-    kfm.add ("operator new", make_unique<kf_operator_new> ());
-    kfm.add ("operator new []", make_unique<kf_operator_new> ());
-    kfm.add ("operator delete", make_unique<kf_operator_delete> (1));
-    kfm.add ("operator delete", make_unique<kf_operator_delete> (2));
-    kfm.add ("operator delete []", make_unique<kf_operator_delete> (1));
-  }
+  /* Language-specific support functions.  */
+  register_known_functions_lang_cp (kfm);
 }
 
 } // namespace ana
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 86bfb4ff8a0..3b93d3e16b8 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -236,56 +236,6 @@ public:
 
 struct append_regions_cb_data;
 
-/* Helper class for handling calls to functions with known behavior.
-   Implemented in region-model-impl-calls.c.  */
-
-class call_details
-{
-public:
-  call_details (const gcall *call, region_model *model,
-		region_model_context *ctxt);
-
-  region_model *get_model () const { return m_model; }
-  region_model_manager *get_manager () const;
-  region_model_context *get_ctxt () const { return m_ctxt; }
-  logger *get_logger () const;
-
-  uncertainty_t *get_uncertainty () const;
-  tree get_lhs_type () const { return m_lhs_type; }
-  const region *get_lhs_region () const { return m_lhs_region; }
-
-  bool maybe_set_lhs (const svalue *result) const;
-
-  unsigned num_args () const;
-  bool arg_is_pointer_p (unsigned idx) const
-  {
-    return POINTER_TYPE_P (get_arg_type (idx));
-  }
-  bool arg_is_size_p (unsigned idx) const;
-
-  const gcall *get_call_stmt () const { return m_call; }
-  location_t get_location () const;
-
-  tree get_arg_tree (unsigned idx) const;
-  tree get_arg_type (unsigned idx) const;
-  const svalue *get_arg_svalue (unsigned idx) const;
-  const char *get_arg_string_literal (unsigned idx) const;
-
-  tree get_fndecl_for_call () const;
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const;
-  void dump (bool simple) const;
-
-  const svalue *get_or_create_conjured_svalue (const region *) const;
-
-private:
-  const gcall *m_call;
-  region_model *m_model;
-  region_model_context *m_ctxt;
-  tree m_lhs_type;
-  const region *m_lhs_region;
-};
-
 /* A region_model encapsulates a representation of the state of memory, with
    a tree of regions, along with their associated values.
    The representation is graph-like because values can be pointers to
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index 799847cb8e8..50e1313f85b 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -47,6 +47,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/program-state.h"
 #include "analyzer/supergraph.h"
 #include "analyzer/analyzer-language.h"
+#include "analyzer/call-details.h"
 #include "analyzer/call-info.h"
 
 #if ENABLE_ANALYZER
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index d2dcb4312a2..1bd594b1263 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/program-point.h"
 #include "analyzer/store.h"
 #include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
 
 #if ENABLE_ANALYZER
 
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 94ca295fae8..b520c9b5566 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/program-point.h"
 #include "analyzer/store.h"
 #include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "analyzer/function-set.h"
diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc
index 519b32d693a..1a3bddee4b2 100644
--- a/gcc/analyzer/varargs.cc
+++ b/gcc/analyzer/varargs.cc
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/diagnostic-manager.h"
 #include "analyzer/exploded-graph.h"
 #include "diagnostic-metadata.h"
+#include "analyzer/call-details.h"
 
 #if ENABLE_ANALYZER
 
diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c
index b424337aad1..57bccf4f2eb 100644
--- a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c
@@ -41,6 +41,7 @@
 #include "analyzer/program-point.h"
 #include "analyzer/store.h"
 #include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
 #include "analyzer/call-info.h"
 #include "make-unique.h"
 
diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c
index 1435b383674..de887dbad83 100644
--- a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c
@@ -41,6 +41,7 @@
 #include "analyzer/program-point.h"
 #include "analyzer/store.h"
 #include "analyzer/region-model.h"
+#include "analyzer/call-details.h"
 #include "analyzer/call-info.h"
 #include "make-unique.h"
 
