diff mbox series

[pushed] analyzer: fix ICE in call summarization [PR114159]

Message ID 20240229231800.3643660-1-dmalcolm@redhat.com
State New
Headers show
Series [pushed] analyzer: fix ICE in call summarization [PR114159] | expand

Commit Message

David Malcolm Feb. 29, 2024, 11:18 p.m. UTC
PR analyzer/114159 reports an ICE inside playback of call summaries
for very low values of --param=analyzer-max-svalue-depth=VAL.

Root cause is that call_summary_edge_info's ctor tries to evaluate
the function ptr of a gimple call stmt and assumes it gets a function *,
but with low values of --param=analyzer-max-svalue-depth=VAL we get
back an UNKNOWN svalue, rather than a pointer to a specific function.

Fix by adding a new call_info ctor that passes a specific
const function & from the call_summary_edge_info, rather than trying
to compute the function.

In doing so, I noticed that the analyzer was using "function *" despite
not modifying functions, and was sloppy about can-be-null versus
must-be-non-null function pointers, so I "constified" the function, and
converted the many places where the function must be non-null to be
"const function &".

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Pushed to trunk as r14-9245-gc0d8a64e72324d.

gcc/analyzer/ChangeLog:
	PR analyzer/114159
	* analyzer.cc: Include "tree-dfa.h".
	(get_ssa_default_def): New decl.
	* analyzer.h (get_ssa_default_def): New.
	* call-info.cc (call_info::call_info): New ctor taking an explicit
	called_fn.
	* call-info.h (call_info::call_info): Likewise.
	* call-summary.cc (call_summary_replay::call_summary_replay):
	Convert param from function * to const function &.
	* call-summary.h (call_summary_replay::call_summary_replay):
	Likewise.
	* checker-event.h (state_change_event::get_dest_function):
	Constify return value.
	* engine.cc (point_and_state::validate): Update for conversion to
	const function &.
	(exploded_node::on_stmt): Likewise.
	(call_summary_edge_info::call_summary_edge_info): Likewise.
	Pass in called_fn to call_info ctor.
	(exploded_node::replay_call_summaries): Update for conversion to
	const function &.  Convert per_function_data from * to &.
	(exploded_node::replay_call_summary): Update for conversion to
	const function &.
	(exploded_graph::add_function_entry): Likewise.
	(toplevel_function_p): Likewise.
	(add_tainted_args_callback): Likewise.
	(exploded_graph::build_initial_worklist): Likewise.
	(exploded_graph::maybe_create_dynamic_call): Likewise.
	(maybe_update_for_edge): Likewise.
	(exploded_graph::on_escaped_function): Likewise.
	* exploded-graph.h (exploded_node::replay_call_summaries):
	Likewise.
	(exploded_node::replay_call_summary): Likewise.
	(exploded_graph::add_function_entry): Likewise.
	* program-point.cc (function_point::from_function_entry):
	Likewise.
	(program_point::from_function_entry): Likewise.
	* program-point.h (function_point::from_function_entry): Likewise.
	(program_point::from_function_entry): Likewise.
	* program-state.cc (program_state::push_frame): Likewise.
	(program_state::get_current_function): Constify return type.
	* program-state.h (program_state::push_frame): Update for
	conversion to const function &.
	(program_state::get_current_function): Likewise.
	* region-model-manager.cc
	(region_model_manager::get_frame_region): Likewise.
	* region-model-manager.h
	(region_model_manager::get_frame_region): Likewise.
	* region-model.cc (region_model::called_from_main_p): Likewise.
	(region_model::update_for_gcall): Likewise.
	(region_model::push_frame): Likewise.
	(region_model::get_current_function): Constify return type.
	(region_model::pop_frame): Update for conversion to
	const function &.
	(selftest::test_stack_frames): Likewise.
	(selftest::test_get_representative_path_var): Likewise.
	(selftest::test_state_merging): Likewise.
	(selftest::test_alloca): Likewise.
	* region-model.h (region_model::push_frame): Likewise.
	(region_model::get_current_function): Likewise.
	* region.cc (frame_region::dump_to_pp): Likewise.
	(frame_region::get_region_for_local): Likewise.
	* region.h (class frame_region): Likewise.
	* sm-signal.cc (signal_unsafe_call::describe_state_change):
	Likewise.
	(update_model_for_signal_handler): Likewise.
	(signal_delivery_edge_info_t::update_model): Likewise.
	(register_signal_handler::impl_transition): Likewise.
	* state-purge.cc (class gimple_op_visitor): Likewise.
	(state_purge_map::state_purge_map): Likewise.
	(state_purge_map::get_or_create_data_for_decl): Likewise.
	(state_purge_per_ssa_name::state_purge_per_ssa_name): Likewise.
	(state_purge_per_ssa_name::add_to_worklist): Likewise.
	(state_purge_per_ssa_name::process_point): Likewise.
	(state_purge_per_decl::add_to_worklist): Likewise.
	(state_purge_annotator::print_needed): Likewise.
	* state-purge.h
	(state_purge_map::get_or_create_data_for_decl): Likewise.
	(class state_purge_per_tree): Likewise.
	(class state_purge_per_ssa_name): Likewise.
	(class state_purge_per_decl): Likewise.
	* supergraph.cc (supergraph::dump_dot_to_pp): Likewise.
	* supergraph.h
	(supergraph::get_node_for_function_entry): Likewise.
	(supergraph::get_node_for_function_exit): Likewise.

gcc/ChangeLog:
	PR analyzer/114159
	* function.cc (function_name): Make param const.
	* function.h (function_name): Likewise.

gcc/testsuite/ChangeLog:
	PR analyzer/114159
	* c-c++-common/analyzer/call-summaries-pr114159.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/analyzer.cc                      |  9 ++
 gcc/analyzer/analyzer.h                       |  3 +
 gcc/analyzer/call-info.cc                     |  8 ++
 gcc/analyzer/call-info.h                      |  1 +
 gcc/analyzer/call-summary.cc                  |  6 +-
 gcc/analyzer/call-summary.h                   |  2 +-
 gcc/analyzer/checker-event.h                  |  2 +-
 gcc/analyzer/engine.cc                        | 83 ++++++++++---------
 gcc/analyzer/exploded-graph.h                 |  8 +-
 gcc/analyzer/program-point.cc                 |  4 +-
 gcc/analyzer/program-point.h                  |  4 +-
 gcc/analyzer/program-state.cc                 |  4 +-
 gcc/analyzer/program-state.h                  |  4 +-
 gcc/analyzer/region-model-manager.cc          |  2 +-
 gcc/analyzer/region-model-manager.h           |  2 +-
 gcc/analyzer/region-model.cc                  | 42 +++++-----
 gcc/analyzer/region-model.h                   |  4 +-
 gcc/analyzer/region.cc                        | 10 +--
 gcc/analyzer/region.h                         | 18 ++--
 gcc/analyzer/sm-signal.cc                     | 13 +--
 gcc/analyzer/state-purge.cc                   | 29 ++++---
 gcc/analyzer/state-purge.h                    | 15 ++--
 gcc/analyzer/supergraph.cc                    |  5 +-
 gcc/analyzer/supergraph.h                     |  8 +-
 gcc/function.cc                               |  2 +-
 gcc/function.h                                |  2 +-
 .../analyzer/call-summaries-pr114159.c        | 20 +++++
 27 files changed, 181 insertions(+), 129 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/analyzer/call-summaries-pr114159.c
diff mbox series

Patch

diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 389496cd57ab..7f5d3d56d338 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "analyzer/analyzer.h"
 #include "tree-pretty-print.h"
 #include "diagnostic-event-id.h"
+#include "tree-dfa.h"
 
 #if ENABLE_ANALYZER
 
@@ -275,6 +276,14 @@  byte_offset_to_json (const byte_offset_t &offset)
   return new json::string (pp_formatted_text (&pp));
 }
 
+/* Workaround for lack of const-correctness of ssa_default_def.  */
+
+tree
+get_ssa_default_def (const function &fun, tree var)
+{
+  return ssa_default_def (const_cast <function *> (&fun), var);
+}
+
 } // namespace ana
 
 /* Helper function for checkers.  Is the CALL to the given function name,
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 20a8e3f9a1d0..1d792ed23ec7 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -433,6 +433,9 @@  compare_constants (tree lhs_const, enum tree_code op, tree rhs_const);
 extern tree
 get_string_cst_size (const_tree string_cst);
 
+extern tree
+get_ssa_default_def (const function &fun, tree var);
+
 } // namespace ana
 
 extern bool is_special_named_call_p (const gcall *call, const char *funcname,
diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc
index 7ec82124d523..4c965b2136c0 100644
--- a/gcc/analyzer/call-info.cc
+++ b/gcc/analyzer/call-info.cc
@@ -143,6 +143,14 @@  call_info::call_info (const call_details &cd)
   gcc_assert (m_fndecl);
 }
 
+call_info::call_info (const call_details &cd,
+		      const function &called_fn)
+: m_call_stmt (cd.get_call_stmt ()),
+  m_fndecl (called_fn.decl)
+{
+  gcc_assert (m_fndecl);
+}
+
 /* class succeed_or_fail_call_info : public call_info.  */
 
 label_text
diff --git a/gcc/analyzer/call-info.h b/gcc/analyzer/call-info.h
index 9815b4141c34..17d5fdfec735 100644
--- a/gcc/analyzer/call-info.h
+++ b/gcc/analyzer/call-info.h
@@ -44,6 +44,7 @@  public:
 
 protected:
   call_info (const call_details &cd);
+  call_info (const call_details &cd, const function &called_fn);
 
 private:
   const gcall *m_call_stmt;
diff --git a/gcc/analyzer/call-summary.cc b/gcc/analyzer/call-summary.cc
index ecb6fb13c9ec..8b8567a15280 100644
--- a/gcc/analyzer/call-summary.cc
+++ b/gcc/analyzer/call-summary.cc
@@ -167,7 +167,7 @@  call_summary::dump (const extrinsic_state &ext_state, bool simple) const
    arguments at the caller. */
 
 call_summary_replay::call_summary_replay (const call_details &cd,
-					  function *called_fn,
+					  const function &called_fn,
 					  call_summary *summary,
 					  const extrinsic_state &ext_state)
 : m_cd (cd),
@@ -177,7 +177,7 @@  call_summary_replay::call_summary_replay (const call_details &cd,
   region_model_manager *mgr = cd.get_manager ();
 
   // populate params based on args
-  tree fndecl = called_fn->decl;
+  tree fndecl = called_fn.decl;
 
   /* Get a frame_region for use with respect to the summary.
      This will be a top-level frame, since that's what's in
@@ -196,7 +196,7 @@  call_summary_replay::call_summary_replay (const call_details &cd,
 	break;
       const svalue *caller_arg_sval = cd.get_arg_svalue (idx);
       tree parm_lval = iter_parm;
-      if (tree parm_default_ssa = ssa_default_def (called_fn, iter_parm))
+      if (tree parm_default_ssa = get_ssa_default_def (called_fn, iter_parm))
 	parm_lval = parm_default_ssa;
       const region *summary_parm_reg
 	= summary_frame->get_region_for_local (mgr, parm_lval, cd.get_ctxt ());
diff --git a/gcc/analyzer/call-summary.h b/gcc/analyzer/call-summary.h
index 73f21ac72826..220dd0831846 100644
--- a/gcc/analyzer/call-summary.h
+++ b/gcc/analyzer/call-summary.h
@@ -68,7 +68,7 @@  class call_summary_replay
 {
 public:
   call_summary_replay (const call_details &cd,
-		       function *called_fn,
+		       const function &called_fn,
 		       call_summary *m_summary,
 		       const extrinsic_state &ext_state);
 
diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h
index 7d8915c8e530..d2fb87fb523f 100644
--- a/gcc/analyzer/checker-event.h
+++ b/gcc/analyzer/checker-event.h
@@ -370,7 +370,7 @@  public:
   label_text get_desc (bool can_colorize) const final override;
   meaning get_meaning () const override;
 
-  function *get_dest_function () const
+  const function *get_dest_function () const
   {
     return m_dst_state.get_current_function ();
   }
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 44ff20cf9af7..ad310b4d8731 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1072,7 +1072,7 @@  point_and_state::validate (const extrinsic_state &ext_state) const
     {
       int index = iter_frame->get_index ();
       gcc_assert (m_point.get_function_at_depth (index)
-		  == iter_frame->get_function ());
+		  == &iter_frame->get_function ());
     }
 }
 
@@ -1496,14 +1496,17 @@  exploded_node::on_stmt (exploded_graph &eg,
 	per_function_data *called_fn_data
 	  = eg.get_per_function_data (called_fn);
 	if (called_fn_data)
-	  return replay_call_summaries (eg,
-					snode,
-					as_a <const gcall *> (stmt),
-					state,
-					path_ctxt,
-					called_fn,
-					called_fn_data,
-					&ctxt);
+	  {
+	    gcc_assert (called_fn);
+	    return replay_call_summaries (eg,
+					  snode,
+					  as_a <const gcall *> (stmt),
+					  state,
+					  path_ctxt,
+					  *called_fn,
+					  *called_fn_data,
+					  &ctxt);
+	  }
       }
 
   bool unknown_side_effects = false;
@@ -1610,10 +1613,10 @@  class call_summary_edge_info : public call_info
 {
 public:
   call_summary_edge_info (const call_details &cd,
-			  function *called_fn,
+			  const function &called_fn,
 			  call_summary *summary,
 			  const extrinsic_state &ext_state)
-  : call_info (cd),
+  : call_info (cd, called_fn),
     m_called_fn (called_fn),
     m_summary (summary),
     m_ext_state (ext_state)
@@ -1648,7 +1651,7 @@  public:
   }
 
 private:
-  function *m_called_fn;
+  const function &m_called_fn;
   call_summary *m_summary;
   const extrinsic_state &m_ext_state;
 };
@@ -1662,18 +1665,15 @@  exploded_node::replay_call_summaries (exploded_graph &eg,
 				      const gcall *call_stmt,
 				      program_state *state,
 				      path_context *path_ctxt,
-				      function *called_fn,
-				      per_function_data *called_fn_data,
+				      const function &called_fn,
+				      per_function_data &called_fn_data,
 				      region_model_context *ctxt)
 {
   logger *logger = eg.get_logger ();
   LOG_SCOPE (logger);
 
-  gcc_assert (called_fn);
-  gcc_assert (called_fn_data);
-
   /* Each summary will call bifurcate on the PATH_CTXT.  */
-  for (auto summary : called_fn_data->m_summaries)
+  for (auto summary : called_fn_data.m_summaries)
     replay_call_summary (eg, snode, call_stmt, state,
 			 path_ctxt, called_fn, summary, ctxt);
   path_ctxt->terminate_path ();
@@ -1691,7 +1691,7 @@  exploded_node::replay_call_summary (exploded_graph &eg,
 				    const gcall *call_stmt,
 				    program_state *old_state,
 				    path_context *path_ctxt,
-				    function *called_fn,
+				    const function &called_fn,
 				    call_summary *summary,
 				    region_model_context *ctxt)
 {
@@ -1700,13 +1700,12 @@  exploded_node::replay_call_summary (exploded_graph &eg,
   gcc_assert (snode);
   gcc_assert (call_stmt);
   gcc_assert (old_state);
-  gcc_assert (called_fn);
   gcc_assert (summary);
 
   if (logger)
     logger->log ("using %s as summary for call to %qE from %qE",
 		 summary->get_desc ().get (),
-		 called_fn->decl,
+		 called_fn.decl,
 		 snode->get_function ()->decl);
   const extrinsic_state &ext_state = eg.get_ext_state ();
   const program_state &summary_end_state = summary->get_state ();
@@ -2784,16 +2783,17 @@  private:
    Return the exploded_node for the entrypoint to the function.  */
 
 exploded_node *
-exploded_graph::add_function_entry (function *fun)
+exploded_graph::add_function_entry (const function &fun)
 {
-  gcc_assert (gimple_has_body_p (fun->decl));
+  gcc_assert (gimple_has_body_p (fun.decl));
 
   /* Be idempotent.  */
-  if (m_functions_with_enodes.contains (fun))
+  function *key = const_cast<function *> (&fun);
+  if (m_functions_with_enodes.contains (key))
     {
       logger * const logger = get_logger ();
        if (logger)
-	logger->log ("entrypoint for %qE already exists", fun->decl);
+	logger->log ("entrypoint for %qE already exists", fun.decl);
       return NULL;
     }
 
@@ -2805,10 +2805,10 @@  exploded_graph::add_function_entry (function *fun)
 
   std::unique_ptr<custom_edge_info> edge_info = NULL;
 
-  if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun->decl)))
+  if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl)))
     {
-      if (mark_params_as_tainted (&state, fun->decl, m_ext_state))
-	edge_info = make_unique<tainted_args_function_info> (fun->decl);
+      if (mark_params_as_tainted (&state, fun.decl, m_ext_state))
+	edge_info = make_unique<tainted_args_function_info> (fun.decl);
     }
 
   if (!state.m_valid)
@@ -2820,7 +2820,7 @@  exploded_graph::add_function_entry (function *fun)
 
   add_edge (m_origin, enode, NULL, false, std::move (edge_info));
 
-  m_functions_with_enodes.add (fun);
+  m_functions_with_enodes.add (key);
 
   return enode;
 }
@@ -3108,7 +3108,7 @@  exploded_graph::get_per_function_data (function *fun) const
    called via other functions.  */
 
 static bool
-toplevel_function_p (function *fun, logger *logger)
+toplevel_function_p (const function &fun, logger *logger)
 {
   /* Don't directly traverse into functions that have an "__analyzer_"
      prefix.  Doing so is useful for the analyzer test suite, allowing
@@ -3119,17 +3119,17 @@  toplevel_function_p (function *fun, logger *logger)
      excess messages from the case of the first function being traversed
      directly.  */
 #define ANALYZER_PREFIX "__analyzer_"
-  if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun->decl)), ANALYZER_PREFIX,
+  if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun.decl)), ANALYZER_PREFIX,
 		strlen (ANALYZER_PREFIX)))
     {
       if (logger)
 	logger->log ("not traversing %qE (starts with %qs)",
-		     fun->decl, ANALYZER_PREFIX);
+		     fun.decl, ANALYZER_PREFIX);
       return false;
     }
 
   if (logger)
-    logger->log ("traversing %qE (all checks passed)", fun->decl);
+    logger->log ("traversing %qE (all checks passed)", fun.decl);
 
   return true;
 }
@@ -3254,9 +3254,9 @@  add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl,
 
   program_point point
     = program_point::from_function_entry (*ext_state.get_model_manager (),
-					  eg->get_supergraph (), fun);
+					  eg->get_supergraph (), *fun);
   program_state state (ext_state);
-  state.push_frame (ext_state, fun);
+  state.push_frame (ext_state, *fun);
 
   if (!mark_params_as_tainted (&state, fndecl, ext_state))
     return;
@@ -3330,9 +3330,10 @@  exploded_graph::build_initial_worklist ()
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
   {
     function *fun = node->get_fun ();
-    if (!toplevel_function_p (fun, logger))
+    gcc_assert (fun);
+    if (!toplevel_function_p (*fun, logger))
       continue;
-    exploded_node *enode = add_function_entry (fun);
+    exploded_node *enode = add_function_entry (*fun);
     if (logger)
       {
 	if (enode)
@@ -3838,8 +3839,8 @@  exploded_graph::maybe_create_dynamic_call (const gcall *call,
   if (fun)
     {
       const supergraph &sg = this->get_supergraph ();
-      supernode *sn_entry = sg.get_node_for_function_entry (fun);
-      supernode *sn_exit = sg.get_node_for_function_exit (fun);
+      supernode *sn_entry = sg.get_node_for_function_entry (*fun);
+      supernode *sn_exit = sg.get_node_for_function_exit (*fun);
 
       program_point new_point
 	= program_point::before_supernode (sn_entry,
@@ -4962,7 +4963,7 @@  maybe_update_for_edge (logger *logger,
 		      == PK_BEFORE_SUPERNODE);
 	  function *fun = eedge->m_dest->get_function ();
 	  gcc_assert (fun);
-	  m_model.push_frame (fun, NULL, ctxt);
+	  m_model.push_frame (*fun, NULL, ctxt);
 	  if (logger)
 	    logger->log ("  pushing frame for %qD", fun->decl);
 	}
@@ -5582,7 +5583,7 @@  exploded_graph::on_escaped_function (tree fndecl)
   if (!gimple_has_body_p (fndecl))
     return;
 
-  exploded_node *enode = add_function_entry (fun);
+  exploded_node *enode = add_function_entry (*fun);
   if (logger)
     {
       if (enode)
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 387ae3a3aea0..642d69bbcc0a 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -285,15 +285,15 @@  class exploded_node : public dnode<eg_traits>
 				       const gcall *call_stmt,
 				       program_state *state,
 				       path_context *path_ctxt,
-				       function *called_fn,
-				       per_function_data *called_fn_data,
+				       const function &called_fn,
+				       per_function_data &called_fn_data,
 				       region_model_context *ctxt);
   void replay_call_summary (exploded_graph &eg,
 			    const supernode *snode,
 			    const gcall *call_stmt,
 			    program_state *state,
 			    path_context *path_ctxt,
-			    function *called_fn,
+			    const function &called_fn,
 			    call_summary *summary,
 			    region_model_context *ctxt);
 
@@ -810,7 +810,7 @@  public:
 
   exploded_node *get_origin () const { return m_origin; }
 
-  exploded_node *add_function_entry (function *fun);
+  exploded_node *add_function_entry (const function &fun);
 
   void build_initial_worklist ();
   void process_worklist ();
diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
index 5e98b5234dcc..6e225cfe7254 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -230,7 +230,7 @@  function_point::final_stmt_p () const
 /* Create a function_point representing the entrypoint of function FUN.  */
 
 function_point
-function_point::from_function_entry (const supergraph &sg, function *fun)
+function_point::from_function_entry (const supergraph &sg, const function &fun)
 {
   return before_supernode (sg.get_node_for_function_entry (fun), NULL);
 }
@@ -698,7 +698,7 @@  program_point::origin (const region_model_manager &mgr)
 program_point
 program_point::from_function_entry (const region_model_manager &mgr,
 				    const supergraph &sg,
-				    function *fun)
+				    const function &fun)
 {
   return program_point (function_point::from_function_entry (sg, fun),
 			mgr.get_empty_call_string ());
diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h
index 62c2c946412d..61b895f4499b 100644
--- a/gcc/analyzer/program-point.h
+++ b/gcc/analyzer/program-point.h
@@ -112,7 +112,7 @@  public:
   /* Factory functions for making various kinds of program_point.  */
 
   static function_point from_function_entry (const supergraph &sg,
-					     function *fun);
+					     const function &fun);
 
   static function_point before_supernode (const supernode *supernode,
 					  const superedge *from_edge);
@@ -252,7 +252,7 @@  public:
   static program_point origin (const region_model_manager &mgr);
   static program_point from_function_entry (const region_model_manager &mgr,
 					    const supergraph &sg,
-					    function *fun);
+					    const function &fun);
 
   static program_point before_supernode (const supernode *supernode,
 					 const superedge *from_edge,
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 55dd6ca71667..c88652baf5ca 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1143,14 +1143,14 @@  program_state::to_json (const extrinsic_state &ext_state) const
 
 void
 program_state::push_frame (const extrinsic_state &ext_state ATTRIBUTE_UNUSED,
-			   function *fun)
+			   const function &fun)
 {
   m_region_model->push_frame (fun, NULL, NULL);
 }
 
 /* Get the current function of this state.  */
 
-function *
+const function *
 program_state::get_current_function () const
 {
   return m_region_model->get_current_function ();
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 69bf931f070c..3ba6a931cd6f 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -226,8 +226,8 @@  public:
 
   json::object *to_json (const extrinsic_state &ext_state) const;
 
-  void push_frame (const extrinsic_state &ext_state, function *fun);
-  function * get_current_function () const;
+  void push_frame (const extrinsic_state &ext_state, const function &fun);
+  const function * get_current_function () const;
 
   void push_call (exploded_graph &eg,
 		  exploded_node *enode,
diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index 93e72ec45a85..8530f49e74ab 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -1676,7 +1676,7 @@  region_model_manager::get_cast_region (const region *original_region,
 
 const frame_region *
 region_model_manager::get_frame_region (const frame_region *calling_frame,
-					function *fun)
+					const function &fun)
 {
   int index = calling_frame ? calling_frame->get_index () + 1 : 0;
 
diff --git a/gcc/analyzer/region-model-manager.h b/gcc/analyzer/region-model-manager.h
index 5c89de1f7c13..7d1208c70a70 100644
--- a/gcc/analyzer/region-model-manager.h
+++ b/gcc/analyzer/region-model-manager.h
@@ -131,7 +131,7 @@  public:
   const region *get_cast_region (const region *original_region,
 				 tree type);
   const frame_region *get_frame_region (const frame_region *calling_frame,
-					function *fun);
+					const function &fun);
   const region *get_symbolic_region (const svalue *sval);
   const string_region *get_region_for_string (tree string_cst);
   const region *get_bit_range (const region *parent, tree type,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 6ab917465d6f..33a4584841e4 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -2619,7 +2619,7 @@  region_model::called_from_main_p () const
   /* Determine if the oldest stack frame in this model is for "main".  */
   const frame_region *frame0 = get_frame_at_index (0);
   gcc_assert (frame0);
-  return id_equal (DECL_NAME (frame0->get_function ()->decl), "main");
+  return id_equal (DECL_NAME (frame0->get_function ().decl), "main");
 }
 
 /* Subroutine of region_model::get_store_value for when REG is (or is within)
@@ -5552,7 +5552,8 @@  region_model::update_for_gcall (const gcall *call_stmt,
     callee = DECL_STRUCT_FUNCTION (fn_decl);
   }
 
-  push_frame (callee, &arg_svals, ctxt);
+  gcc_assert (callee);
+  push_frame (*callee, &arg_svals, ctxt);
 }
 
 /* Pop the top-most frame_region from the stack, and copy the return
@@ -5896,14 +5897,15 @@  region_model::on_top_level_param (tree param,
    Return the frame_region for the new frame.  */
 
 const region *
-region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
+region_model::push_frame (const function &fun,
+			  const vec<const svalue *> *arg_svals,
 			  region_model_context *ctxt)
 {
   m_current_frame = m_mgr->get_frame_region (m_current_frame, fun);
   if (arg_svals)
     {
       /* Arguments supplied from a caller frame.  */
-      tree fndecl = fun->decl;
+      tree fndecl = fun.decl;
       unsigned idx = 0;
       for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
 	   iter_parm = DECL_CHAIN (iter_parm), ++idx)
@@ -5914,7 +5916,7 @@  region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
 	  if (idx >= arg_svals->length ())
 	    break;
 	  tree parm_lval = iter_parm;
-	  if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
+	  if (tree parm_default_ssa = get_ssa_default_def (fun, iter_parm))
 	    parm_lval = parm_default_ssa;
 	  const region *parm_reg = get_lvalue (parm_lval, ctxt);
 	  const svalue *arg_sval = (*arg_svals)[idx];
@@ -5937,7 +5939,7 @@  region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
       /* Otherwise we have a top-level call within the analysis.  The params
 	 have defined but unknown initial values.
 	 Anything they point to has escaped.  */
-      tree fndecl = fun->decl;
+      tree fndecl = fun.decl;
 
       /* Handle "__attribute__((nonnull))".   */
       tree fntype = TREE_TYPE (fndecl);
@@ -5951,7 +5953,7 @@  region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
 			   ? (bitmap_empty_p (nonnull_args)
 			      || bitmap_bit_p (nonnull_args, parm_idx))
 			   : false);
-	  if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
+	  if (tree parm_default_ssa = get_ssa_default_def (fun, iter_parm))
 	    on_top_level_param (parm_default_ssa, non_null, ctxt);
 	  else
 	    on_top_level_param (iter_parm, non_null, ctxt);
@@ -5967,12 +5969,12 @@  region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
 /* Get the function of the top-most frame in this region_model's stack.
    There must be such a frame.  */
 
-function *
+const function *
 region_model::get_current_function () const
 {
   const frame_region *frame = get_current_frame ();
   gcc_assert (frame);
-  return frame->get_function ();
+  return &frame->get_function ();
 }
 
 /* Pop the topmost frame_region from this region_model's stack;
@@ -6007,7 +6009,7 @@  region_model::pop_frame (tree result_lvalue,
     ctxt->on_pop_frame (frame_reg);
 
   /* Evaluate the result, within the callee frame.  */
-  tree fndecl = m_current_frame->get_function ()->decl;
+  tree fndecl = m_current_frame->get_function ().decl;
   tree result = DECL_RESULT (fndecl);
   const svalue *retval = NULL;
   if (result
@@ -7966,7 +7968,7 @@  test_stack_frames ()
 
   /* Push stack frame for "parent_fn".  */
   const region *parent_frame_reg
-    = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl),
+    = model.push_frame (*DECL_STRUCT_FUNCTION (parent_fndecl),
 			NULL, &ctxt);
   ASSERT_EQ (model.get_current_frame (), parent_frame_reg);
   ASSERT_TRUE (model.region_exists_p (parent_frame_reg));
@@ -7982,7 +7984,7 @@  test_stack_frames ()
 
   /* Push stack frame for "child_fn".  */
   const region *child_frame_reg
-    = model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
+    = model.push_frame (*DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
   ASSERT_EQ (model.get_current_frame (), child_frame_reg);
   ASSERT_TRUE (model.region_exists_p (child_frame_reg));
   const region *x_in_child_reg = model.get_lvalue (x, &ctxt);
@@ -8075,7 +8077,7 @@  test_get_representative_path_var ()
   for (int depth = 0; depth < 5; depth++)
     {
       const region *frame_n_reg
-	= model.push_frame (DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt);
+	= model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt);
       const region *parm_n_reg = model.get_lvalue (path_var (n, depth), &ctxt);
       parm_regs.safe_push (parm_n_reg);
 
@@ -8319,9 +8321,9 @@  test_state_merging ()
     region_model model0 (&mgr);
     region_model model1 (&mgr);
     ASSERT_EQ (model0.get_stack_depth (), 0);
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
+    model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
     ASSERT_EQ (model0.get_stack_depth (), 1);
-    model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
+    model1.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
 
     placeholder_svalue test_sval (mgr.alloc_symbol_id (),
 				  integer_type_node, "test sval");
@@ -8413,7 +8415,7 @@  test_state_merging ()
   /* Pointers: non-NULL and non-NULL: ptr to a local.  */
   {
     region_model model0 (&mgr);
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
+    model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     model0.set_value (model0.get_lvalue (p, NULL),
 		      model0.get_rvalue (addr_of_a, NULL), NULL);
 
@@ -8552,12 +8554,12 @@  test_state_merging ()
      frame points to a local in a more recent stack frame.  */
   {
     region_model model0 (&mgr);
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
+    model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     const region *q_in_first_frame = model0.get_lvalue (q, NULL);
 
     /* Push a second frame.  */
     const region *reg_2nd_frame
-      = model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
+      = model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
 
     /* Have a pointer in the older frame point to a local in the
        more recent frame.  */
@@ -8584,7 +8586,7 @@  test_state_merging ()
   /* Verify that we can merge a model in which a local points to a global.  */
   {
     region_model model0 (&mgr);
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
+    model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     model0.set_value (model0.get_lvalue (q, NULL),
 		      model0.get_rvalue (addr_of_y, NULL), NULL);
 
@@ -9110,7 +9112,7 @@  test_alloca ()
 
   /* Push stack frame.  */
   const region *frame_reg
-    = model.push_frame (DECL_STRUCT_FUNCTION (fndecl),
+    = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl),
 			NULL, &ctxt);
   /* "p = alloca (n * 4);".  */
   const svalue *size_sval = model.get_rvalue (n_times_4, &ctxt);
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index d4ef10120dd5..118e0f254273 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -347,10 +347,10 @@  class region_model
   void update_for_return_gcall (const gcall *call_stmt,
                                 region_model_context *ctxt);
 
-  const region *push_frame (function *fun, const vec<const svalue *> *arg_sids,
+  const region *push_frame (const function &fun, const vec<const svalue *> *arg_sids,
 			    region_model_context *ctxt);
   const frame_region *get_current_frame () const { return m_current_frame; }
-  function * get_current_function () const;
+  const function *get_current_function () const;
   void pop_frame (tree result_lvalue,
 		  const svalue **out_result,
 		  region_model_context *ctxt,
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index 50821a592302..d45706141fb0 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -1306,10 +1306,10 @@  void
 frame_region::dump_to_pp (pretty_printer *pp, bool simple) const
 {
   if (simple)
-    pp_printf (pp, "frame: %qs@%i", function_name (m_fun), get_stack_depth ());
+    pp_printf (pp, "frame: %qs@%i", function_name (&m_fun), get_stack_depth ());
   else
     pp_printf (pp, "frame_region(%qs, index: %i, depth: %i)",
-	       function_name (m_fun), m_index, get_stack_depth ());
+	       function_name (&m_fun), m_index, get_stack_depth ());
 }
 
 const decl_region *
@@ -1334,14 +1334,14 @@  frame_region::get_region_for_local (region_model_manager *mgr,
 	  /* Fall through.  */
 	case PARM_DECL:
 	case RESULT_DECL:
-	  gcc_assert (DECL_CONTEXT (expr) == m_fun->decl);
+	  gcc_assert (DECL_CONTEXT (expr) == m_fun.decl);
 	  break;
 	case SSA_NAME:
 	  {
 	    if (tree var = SSA_NAME_VAR (expr))
 	      {
 		if (DECL_P (var))
-		  gcc_assert (DECL_CONTEXT (var) == m_fun->decl);
+		  gcc_assert (DECL_CONTEXT (var) == m_fun.decl);
 	      }
 	    else if (ctxt)
 	      if (const extrinsic_state *ext_state = ctxt->get_ext_state ())
@@ -1351,7 +1351,7 @@  frame_region::get_region_for_local (region_model_manager *mgr,
 		    const gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
 		    const supernode *snode
 		      = sg->get_supernode_for_stmt (def_stmt);
-		    gcc_assert (snode->get_function () == m_fun);
+		    gcc_assert (snode->get_function () == &m_fun);
 		  }
 	  }
 	  break;
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index 70557babbc04..73f35f55ba71 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -305,11 +305,10 @@  public:
   /* A support class for uniquifying instances of frame_region.  */
   struct key_t
   {
-    key_t (const frame_region *calling_frame, function *fun)
-    : m_calling_frame (calling_frame), m_fun (fun)
+    key_t (const frame_region *calling_frame, const function &fun)
+    : m_calling_frame (calling_frame), m_fun (&fun)
     {
       /* calling_frame can be NULL.  */
-      gcc_assert (fun);
     }
 
     hashval_t hash () const
@@ -322,7 +321,8 @@  public:
 
     bool operator== (const key_t &other) const
     {
-      return (m_calling_frame == other.m_calling_frame && m_fun == other.m_fun);
+      return (m_calling_frame == other.m_calling_frame
+	      && m_fun == other.m_fun);
     }
 
     void mark_deleted () { m_fun = reinterpret_cast<function *> (1); }
@@ -334,12 +334,12 @@  public:
     bool is_empty () const { return m_fun == NULL; }
 
     const frame_region *m_calling_frame;
-    function *m_fun;
+    const function *m_fun;
   };
 
   frame_region (symbol::id_t id, const region *parent,
 		const frame_region *calling_frame,
-		function *fun, int index)
+		const function &fun, int index)
   : space_region (id, parent), m_calling_frame (calling_frame),
     m_fun (fun), m_index (index)
   {}
@@ -356,8 +356,8 @@  public:
 
   /* Accessors.  */
   const frame_region *get_calling_frame () const { return m_calling_frame; }
-  function *get_function () const { return m_fun; }
-  tree get_fndecl () const { return get_function ()->decl; }
+  const function &get_function () const { return m_fun; }
+  tree get_fndecl () const { return get_function ().decl; }
   int get_index () const { return m_index; }
   int get_stack_depth () const { return m_index + 1; }
 
@@ -373,7 +373,7 @@  public:
 
  private:
   const frame_region *m_calling_frame;
-  function *m_fun;
+  const function &m_fun;
   int m_index;
 
   /* The regions for the decls within this frame are managed by this
diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
index 5be857a99ff8..93269caa0dc5 100644
--- a/gcc/analyzer/sm-signal.cc
+++ b/gcc/analyzer/sm-signal.cc
@@ -146,7 +146,8 @@  public:
     if (change.is_global_p ()
 	&& change.m_new_state == m_sm.m_in_signal_handler)
       {
-	function *handler = change.m_event.get_dest_function ();
+	const function *handler = change.m_event.get_dest_function ();
+	gcc_assert (handler);
 	return change.formatted_print ("registering %qD as signal handler",
 				       handler->decl);
       }
@@ -193,7 +194,7 @@  signal_state_machine::signal_state_machine (logger *logger)
 
 static void
 update_model_for_signal_handler (region_model *model,
-				 function *handler_fun)
+				 const function &handler_fun)
 {
   gcc_assert (model);
   /* Purge all state within MODEL.  */
@@ -222,7 +223,9 @@  public:
 		     region_model_context *) const final override
   {
     gcc_assert (eedge);
-    update_model_for_signal_handler (model, eedge->m_dest->get_function ());
+    gcc_assert (eedge->m_dest->get_function ());
+    update_model_for_signal_handler (model,
+				     *eedge->m_dest->get_function ());
     return true;
   }
 
@@ -263,11 +266,11 @@  public:
     program_point entering_handler
       = program_point::from_function_entry (*ext_state.get_model_manager (),
 					    eg->get_supergraph (),
-					    handler_fun);
+					    *handler_fun);
 
     program_state state_entering_handler (ext_state);
     update_model_for_signal_handler (state_entering_handler.m_region_model,
-				     handler_fun);
+				     *handler_fun);
     state_entering_handler.m_checker_states[sm_idx]->set_global_state
       (m_sm.m_in_signal_handler);
 
diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc
index 93959fb08ea3..324b548f75b1 100644
--- a/gcc/analyzer/state-purge.cc
+++ b/gcc/analyzer/state-purge.cc
@@ -89,7 +89,7 @@  class gimple_op_visitor : public log_user
 public:
   gimple_op_visitor (state_purge_map *map,
 		     const function_point &point,
-		     function *fun)
+		     const function &fun)
   : log_user (map->get_logger ()),
     m_map (map),
     m_point (point),
@@ -172,7 +172,7 @@  private:
 
   state_purge_map *m_map;
   const function_point &m_point;
-  function *m_fun;
+  const function &m_fun;
 };
 
 static bool
@@ -214,6 +214,7 @@  state_purge_map::state_purge_map (const supergraph &sg,
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
   {
     function *fun = node->get_fun ();
+    gcc_assert (fun);
     if (logger)
       log ("function: %s", function_name (fun));
     tree name;
@@ -225,7 +226,7 @@  state_purge_map::state_purge_map (const supergraph &sg,
 	  if (TREE_CODE (var) == VAR_DECL)
 	    if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
 	      continue;
-	m_ssa_map.put (name, new state_purge_per_ssa_name (*this, name, fun));
+	m_ssa_map.put (name, new state_purge_per_ssa_name (*this, name, *fun));
       }
   }
 
@@ -241,8 +242,10 @@  state_purge_map::state_purge_map (const supergraph &sg,
       unsigned i;
       FOR_EACH_VEC_ELT (snode->m_stmts, i, stmt)
 	{
+	  function *fun = snode->get_function ();
+	  gcc_assert (fun);
 	  function_point point (function_point::before_stmt (snode, i));
-	  gimple_op_visitor v (this, point, snode->get_function ());
+	  gimple_op_visitor v (this, point, *fun);
 	  walk_stmt_load_store_addr_ops (stmt, &v,
 					 my_load_cb, my_store_cb, my_addr_cb);
 	}
@@ -272,7 +275,7 @@  state_purge_map::~state_purge_map ()
    if necessary.  */
 
 state_purge_per_decl &
-state_purge_map::get_or_create_data_for_decl (function *fun, tree decl)
+state_purge_map::get_or_create_data_for_decl (const function &fun, tree decl)
 {
   if (state_purge_per_decl **slot
       = const_cast <decl_map_t&> (m_decl_map).get (decl))
@@ -295,14 +298,14 @@  state_purge_map::get_or_create_data_for_decl (function *fun, tree decl)
 
 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
 						    tree name,
-						    function *fun)
+						    const function &fun)
 : state_purge_per_tree (fun), m_points_needing_name (), m_name (name)
 {
   LOG_FUNC (map.get_logger ());
 
   if (map.get_logger ())
     {
-      map.log ("SSA name: %qE within %qD", name, fun->decl);
+      map.log ("SSA name: %qE within %qD", name, fun.decl);
 
       /* Show def stmt.  */
       const gimple *def_stmt = SSA_NAME_DEF_STMT (name);
@@ -410,7 +413,7 @@  state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
 
   if (map.get_logger ())
     {
-      map.log ("%qE in %qD is needed to process:", name, fun->decl);
+      map.log ("%qE in %qD is needed to process:", name, fun.decl);
       /* Log m_points_needing_name, sorting it to avoid churn when comparing
 	 dumps.  */
       auto_vec<function_point> points;
@@ -472,7 +475,7 @@  state_purge_per_ssa_name::add_to_worklist (const function_point &point,
       logger->end_log_line ();
     }
 
-  gcc_assert (point.get_function () == get_function ());
+  gcc_assert (point.get_function () == &get_function ());
   if (point.get_from_edge ())
     gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
 
@@ -678,7 +681,7 @@  state_purge_per_ssa_name::process_point (const function_point &point,
 
 state_purge_per_decl::state_purge_per_decl (const state_purge_map &map,
 					    tree decl,
-					    function *fun)
+					    const function &fun)
 : state_purge_per_tree (fun),
   m_decl (decl)
 {
@@ -794,7 +797,7 @@  state_purge_per_decl::add_to_worklist (const function_point &point,
       logger->end_log_line ();
     }
 
-  gcc_assert (point.get_function () == get_function ());
+  gcc_assert (point.get_function () == &get_function ());
   if (point.get_from_edge ())
     gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
 
@@ -1192,7 +1195,7 @@  state_purge_annotator::print_needed (graphviz_out *gv,
     {
       tree name = (*iter).first;
       state_purge_per_ssa_name *per_name_data = (*iter).second;
-      if (per_name_data->get_function () == point.get_function ())
+      if (&per_name_data->get_function () == point.get_function ())
 	{
 	  if (per_name_data->needed_at_point_p (point))
 	    needed.safe_push (name);
@@ -1206,7 +1209,7 @@  state_purge_annotator::print_needed (graphviz_out *gv,
     {
       tree decl = (*iter).first;
       state_purge_per_decl *per_decl_data = (*iter).second;
-      if (per_decl_data->get_function () == point.get_function ())
+      if (&per_decl_data->get_function () == point.get_function ())
 	{
 	  if (per_decl_data->needed_at_point_p (point))
 	    needed.safe_push (decl);
diff --git a/gcc/analyzer/state-purge.h b/gcc/analyzer/state-purge.h
index c6d64b41fd46..4eb2ba0a908a 100644
--- a/gcc/analyzer/state-purge.h
+++ b/gcc/analyzer/state-purge.h
@@ -112,7 +112,8 @@  public:
       return NULL;
   }
 
-  state_purge_per_decl &get_or_create_data_for_decl (function *fun, tree decl);
+  state_purge_per_decl &
+  get_or_create_data_for_decl (const function &fun, tree decl);
 
   const supergraph &get_sg () const { return m_sg; }
 
@@ -135,19 +136,19 @@  private:
 class state_purge_per_tree
 {
 public:
-  function *get_function () const { return m_fun; }
-  tree get_fndecl () const { return m_fun->decl; }
+  const function &get_function () const { return m_fun; }
+  tree get_fndecl () const { return m_fun.decl; }
 
 protected:
   typedef hash_set<function_point> point_set_t;
 
-  state_purge_per_tree (function *fun)
+  state_purge_per_tree (const function &fun)
   : m_fun (fun)
   {
   }
 
 private:
-  function *m_fun;
+  const function &m_fun;
 };
 
 /* The part of a state_purge_map relating to a specific SSA name.
@@ -162,7 +163,7 @@  class state_purge_per_ssa_name : public state_purge_per_tree
 public:
   state_purge_per_ssa_name (const state_purge_map &map,
 			    tree name,
-			    function *fun);
+			    const function &fun);
 
   bool needed_at_point_p (const function_point &point) const;
 
@@ -194,7 +195,7 @@  class state_purge_per_decl : public state_purge_per_tree
 public:
   state_purge_per_decl (const state_purge_map &map,
 			tree decl,
-			function *fun);
+			const function &fun);
 
   bool needed_at_point_p (const function_point &point) const;
 
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index b82275256b72..adbf90f17ede 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -364,6 +364,7 @@  supergraph::dump_dot_to_pp (pretty_printer *pp,
     FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     {
       function *fun = node->get_fun ();
+      gcc_assert (fun);
       const char *funcname = function_name (fun);
       gv.println ("subgraph \"cluster_%s\" {",
 		  funcname);
@@ -409,9 +410,9 @@  supergraph::dump_dot_to_pp (pretty_printer *pp,
 
       /* Add an invisible edge from ENTRY to EXIT, to improve the graph layout.  */
       pp_string (pp, "\t");
-      get_node_for_function_entry (fun)->dump_dot_id (pp);
+      get_node_for_function_entry (*fun)->dump_dot_id (pp);
       pp_string (pp, ":s -> ");
-      get_node_for_function_exit (fun)->dump_dot_id (pp);
+      get_node_for_function_exit (*fun)->dump_dot_id (pp);
       pp_string (pp, ":n [style=\"invis\",constraint=true];\n");
 
       /* Terminate per-function "subgraph" */
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index 2677acad8d6a..86f918bc8b5d 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -111,14 +111,14 @@  public:
   supergraph (logger *logger);
   ~supergraph ();
 
-  supernode *get_node_for_function_entry (function *fun) const
+  supernode *get_node_for_function_entry (const function &fun) const
   {
-    return get_node_for_block (ENTRY_BLOCK_PTR_FOR_FN (fun));
+    return get_node_for_block (ENTRY_BLOCK_PTR_FOR_FN (&fun));
   }
 
-  supernode *get_node_for_function_exit (function *fun) const
+  supernode *get_node_for_function_exit (const function &fun) const
   {
-    return get_node_for_block (EXIT_BLOCK_PTR_FOR_FN (fun));
+    return get_node_for_block (EXIT_BLOCK_PTR_FOR_FN (&fun));
   }
 
   supernode *get_node_for_block (basic_block bb) const
diff --git a/gcc/function.cc b/gcc/function.cc
index 5ffd438475e9..9488181cbd95 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -6391,7 +6391,7 @@  fndecl_name (tree fndecl)
 
 /* Returns the name of function FN.  */
 const char *
-function_name (struct function *fn)
+function_name (const function *fn)
 {
   tree fndecl = (fn == NULL) ? NULL : fn->decl;
   return fndecl_name (fndecl);
diff --git a/gcc/function.h b/gcc/function.h
index 2d775b877fc2..19e15bd63b0a 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -730,7 +730,7 @@  extern poly_int64 get_stack_dynamic_offset ();
 
 /* Returns the name of the current function.  */
 extern const char *fndecl_name (tree);
-extern const char *function_name (struct function *);
+extern const char *function_name (const function *);
 extern const char *current_function_name (void);
 
 extern void used_types_insert (tree);
diff --git a/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr114159.c b/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr114159.c
new file mode 100644
index 000000000000..19b545a1154d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr114159.c
@@ -0,0 +1,20 @@ 
+/* Verify we don't ICE on this case with these options.  */
+
+/* { dg-additional-options "-fanalyzer-call-summaries --param=analyzer-max-svalue-depth=0 -Wno-analyzer-symbol-too-complex" } */
+
+int foo_i;
+void bar() {}
+void foo() {
+  if (foo_i)
+    bar();
+  else
+    goto f1;
+  bar();
+f1:
+  bar();
+}
+int main() {
+  foo();
+  foo();
+  return 0;
+}