From cb03b383ffede9f55339b445ff1bae7bff92ad5b Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 6 Nov 2019 15:45:02 -0500
Subject: [PATCH 114/119] FIXME: add state_change WIP

---
 gcc/analyzer/analyzer.h            |  1 +
 gcc/analyzer/diagnostic-manager.cc |  3 +-
 gcc/analyzer/diagnostic-manager.h  |  1 +
 gcc/analyzer/engine.cc             | 97 ++++++++++++++++++++++++++------------
 gcc/analyzer/exploded-graph.h      | 22 +++++++--
 gcc/analyzer/program-point.h       | 11 ++++-
 gcc/analyzer/program-state.cc      | 95 ++++++++++++++++++++++++++++++++++---
 gcc/analyzer/program-state.h       | 52 +++++++++++++++++++-
 gcc/analyzer/region-model.cc       |  1 +
 gcc/analyzer/region-model.h        |  1 +
 gcc/analyzer/sm.h                  |  4 +-
 gcc/analyzer/state-purge.cc        |  7 +--
 12 files changed, 249 insertions(+), 46 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 900dc10..4934346 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -64,6 +64,7 @@ class exploded_path;
 class analysis_plan;
 class state_purge_map;
 class state_purge_per_ssa_name;
+class state_change;
 
 ////////////////////////////////////////////////////////////////////////////
 
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index a0109c2..546bb46 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -557,7 +557,7 @@ diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge,
       const state_machine &sm = ext_state.get_sm (i);
       const sm_state_map &src_smap = *src_state.m_checker_states[i];
       const sm_state_map &dst_smap = *dst_state.m_checker_states[i];
-      add_state_change_events (eedge, sm, src_smap, dst_smap, emission_path);
+      add_state_change_events (eedge, i, sm, src_smap, dst_smap, emission_path);
     }
 
   /* Add events for rewinding from a longjmp to a setjmp.  */
@@ -705,6 +705,7 @@ diagnostic_manager::add_events_for_superedge (const exploded_edge &eedge,
 
 void
 diagnostic_manager::add_state_change_events (const exploded_edge &eedge,
+					     int sm_idx,
 					     const state_machine &sm,
 					     const sm_state_map &src_smap,
 					     const sm_state_map &dst_smap,
diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
index 638f22a..1521462 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -106,6 +106,7 @@ private:
 				 checker_path *emission_path) const;
 
   void add_state_change_events (const exploded_edge &eedge,
+				int sm_idx,
 				const state_machine &sm,
 				const sm_state_map &src_smap,
 				const sm_state_map &dst_smap,
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 839c4dd..5b00c8f 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -88,6 +88,7 @@ impl_region_model_context (exploded_graph &eg,
 			      old state, rather than the new?  */
 			   const program_state *old_state,
 			   program_state *new_state,
+			   state_change *change,
 
 			   const gimple *stmt,
 			   stmt_finder *stmt_finder)
@@ -95,6 +96,7 @@ impl_region_model_context (exploded_graph &eg,
   m_enode_for_diag (enode_for_diag),
   m_old_state (old_state),
   m_new_state (new_state),
+  m_change (change),
   m_stmt (stmt),
   m_stmt_finder (stmt_finder),
   m_ext_state (eg.get_ext_state ())
@@ -103,10 +105,12 @@ impl_region_model_context (exploded_graph &eg,
 
 impl_region_model_context::
 impl_region_model_context (program_state *state,
+			   state_change *change,
 			   const extrinsic_state &ext_state)
 : log_user (NULL), m_eg (NULL), m_enode_for_diag (NULL),
   m_old_state (NULL),
   m_new_state (state),
+  m_change (change),
   m_stmt (NULL),
   m_stmt_finder (NULL),
   m_ext_state (ext_state)
@@ -216,6 +220,7 @@ class impl_sm_context : public sm_context, public log_user
 {
 public:
   impl_sm_context (exploded_graph &eg,
+		   int sm_idx,
 		   const state_machine &sm,
 		   const exploded_node *enode_for_diag,
 
@@ -223,15 +228,17 @@ public:
 		      old state, rather than the new?  */
 		   const program_state *old_state,
 		   program_state *new_state,
+		   state_change *change,
 
 		   const sm_state_map *old_smap,
 		   sm_state_map *new_smap,
 
 		   stmt_finder *stmt_finder = NULL)
-  : sm_context (sm),
+  : sm_context (sm_idx, sm),
     log_user (eg.get_logger ()),
     m_eg (eg), m_enode_for_diag (enode_for_diag),
     m_old_state (old_state), m_new_state (new_state),
+    m_change (change),
     m_old_smap (old_smap), m_new_smap (new_smap),
     m_stmt_finder (stmt_finder)
   {
@@ -246,12 +253,16 @@ public:
   {
     LOG_FUNC (get_logger ());
     impl_region_model_context old_ctxt
-      (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/, stmt);
+      (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
+       m_change, stmt);
     svalue_id var_old_sid
       = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
+    svalue_id origin_old_sid
+      = m_old_state->m_region_model->get_rvalue (origin, &old_ctxt);
 
     impl_region_model_context new_ctxt (m_eg, m_enode_for_diag,
-					m_old_state, m_new_state, NULL);
+					m_old_state, m_new_state,
+					m_change, NULL);
     svalue_id var_new_sid
       = m_new_state->m_region_model->get_rvalue (var, &new_ctxt);
     svalue_id origin_new_sid
@@ -268,6 +279,10 @@ public:
 	       m_sm.get_state_name (to));
 	m_new_smap->set_state (m_new_state->m_region_model, var_new_sid,
 			       to, origin_new_sid);
+	if (m_change)
+	  m_change->add_sm_change (m_sm_idx, var_old_sid, var_new_sid,
+				   origin_old_sid,
+				   from, to);
       }
   }
 
@@ -279,7 +294,7 @@ public:
     gcc_assert (d); // take ownership
 
     impl_region_model_context old_ctxt
-      (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL);
+      (m_eg, m_enode_for_diag, m_old_state, m_new_state, m_change, NULL);
     svalue_id var_old_sid
       = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
     state_machine::state_t current = m_old_smap->get_state (var_old_sid);
@@ -325,6 +340,7 @@ public:
   const exploded_node *m_enode_for_diag;
   const program_state *m_old_state;
   program_state *m_new_state;
+  state_change *m_change;
   const sm_state_map *m_old_smap;
   sm_state_map *m_new_smap;
   stmt_finder *m_stmt_finder;
@@ -559,8 +575,9 @@ impl_region_model_context::on_state_leak (const state_machine &sm,
     log ("best leaked_tree: %qE", leaked_tree);
 
   leak_stmt_finder stmt_finder (*m_eg, leaked_tree);
-  impl_sm_context sm_ctxt (*m_eg, sm, m_enode_for_diag,
+  impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
 			   m_old_state, m_new_state,
+			   m_change,
 			   m_old_state->m_checker_states[sm_idx],
 			   m_new_state->m_checker_states[sm_idx],
 			   &stmt_finder);
@@ -637,8 +654,9 @@ impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs)
   FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
     {
       const state_machine &sm = m_ext_state.get_sm (sm_idx);
-      impl_sm_context sm_ctxt (*m_eg, sm, m_enode_for_diag,
+      impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
 			       m_old_state, m_new_state,
+			       m_change,
 			       m_old_state->m_checker_states[sm_idx],
 			       m_new_state->m_checker_states[sm_idx]);
       sm.on_condition (&sm_ctxt,
@@ -893,7 +911,8 @@ bool
 exploded_node::on_stmt (exploded_graph &eg,
 			const supernode *snode,
 			const gimple *stmt,
-			program_state *state) const
+			program_state *state,
+			state_change *change) const
 {
   /* Preserve the old state.  It is used here for looking
      up old checker states, for determining state transitions, and
@@ -902,7 +921,7 @@ exploded_node::on_stmt (exploded_graph &eg,
   const program_state old_state (*state);
 
   impl_region_model_context ctxt (eg, this,
-				  &old_state, state,
+				  &old_state, state, change,
 				  stmt);
 
   if (const gassign *assign = dyn_cast <const gassign *> (stmt))
@@ -981,7 +1000,8 @@ exploded_node::on_stmt (exploded_graph &eg,
       const sm_state_map *old_smap
 	= old_state.m_checker_states[sm_idx];
       sm_state_map *new_smap = state->m_checker_states[sm_idx];
-      impl_sm_context ctxt (eg, sm, this, &old_state, state,
+      impl_sm_context ctxt (eg, sm_idx, sm, this, &old_state, state,
+			    change,
 			    old_smap, new_smap);
       /* Allow the state_machine to handle the stmt.  */
       if (!sm.on_stmt (&ctxt, snode, stmt))
@@ -1027,14 +1047,15 @@ bool
 exploded_node::on_edge (exploded_graph &eg,
 			const superedge *succ,
 			program_point *next_point,
-			program_state *next_state) const
+			program_state *next_state,
+			state_change *change) const
 {
   LOG_FUNC (eg.get_logger ());
 
   if (!next_point->on_edge (eg, succ))
     return false;
 
-  if (!next_state->on_edge (eg, *this, succ))
+  if (!next_state->on_edge (eg, *this, succ, change))
     return false;
 
   return true;
@@ -1158,10 +1179,12 @@ exploded_node::on_longjmp (exploded_graph &eg,
 				      setjmp_point.get_call_string ());
 
   exploded_node *next = eg.get_or_create_node (next_point, *new_state);
+  state_change change; // FIXME
 
   /* Create custom exploded_edge for a longjmp.  */
   if (next)
     eg.add_edge (const_cast<exploded_node *> (this), next, NULL,
+		 change,
 		 new rewind_info_t (enode_origin));
 }
 
@@ -1200,7 +1223,9 @@ exploded_node::detect_leaks (exploded_graph &eg) const
 
   purge_stats stats;
   impl_region_model_context ctxt (eg, this,
-				  &old_state, &new_state, get_stmt ());
+				  &old_state, &new_state,
+				  NULL, // FIXME: state_change
+				  get_stmt ());
   new_state.m_region_model->pop_frame (true, &stats, &ctxt);
 }
 
@@ -1239,8 +1264,10 @@ exploded_node::dump_succs_and_preds (FILE *outf) const
 
 exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
 			      const superedge *sedge,
+			      const state_change &change,
 			      rewind_info_t *rewind_info)
-: dedge (src, dest), m_sedge (sedge), m_rewind_info (rewind_info)
+: dedge (src, dest), m_sedge (sedge), m_change (change),
+  m_rewind_info (rewind_info)
 {
 }
 
@@ -1255,7 +1282,7 @@ exploded_edge::~exploded_edge ()
    Use the label of the underlying superedge, if any.  */
 
 void
-exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
+exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const
 {
   pretty_printer *pp = gv->get_pp ();
 
@@ -1302,6 +1329,9 @@ exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
   else if (m_rewind_info)
     pp_string (pp, "rewind");
 
+  m_change.dump (pp, args.m_eg.get_ext_state ());
+  //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+
   pp_printf (pp, "\"];\n");
 }
 
@@ -1667,7 +1697,8 @@ exploded_graph::add_function_entry (function *fun)
   exploded_node *enode = get_or_create_node (point, state);
   /* We should never fail to add such a node.  */
   gcc_assert (enode);
-  add_edge (m_origin, enode, NULL);
+  state_change change;
+  add_edge (m_origin, enode, NULL, change);
   return enode;
 }
 
@@ -1837,9 +1868,10 @@ exploded_graph::get_or_create_node (const program_point &point,
 void
 exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
 			  const superedge *sedge,
+			  const state_change &change,
 			  rewind_info_t *rewind_info)
 {
-  exploded_edge *e = new exploded_edge (src, dest, sedge, rewind_info);
+  exploded_edge *e = new exploded_edge (src, dest, sedge, change, rewind_info);
   digraph::add_edge (e);
 }
 
@@ -2017,6 +2049,7 @@ exploded_graph::process_worklist ()
 		gcc_assert (state != state_2);
 
 		program_state merged_state (m_ext_state);
+		state_change change;
 		if (state.can_merge_with_p (state_2, m_ext_state,
 					    &merged_state))
 		  {
@@ -2026,7 +2059,7 @@ exploded_graph::process_worklist ()
 		    if (merged_state == state)
 		      {
 			/* Then merge node_2 into node by adding an edge.  */
-			add_edge (node_2, node, NULL);
+			add_edge (node_2, node, NULL, change);
 
 			/* Remove node_2 from the worklist.  */
 			m_worklist.take_next ();
@@ -2038,7 +2071,7 @@ exploded_graph::process_worklist ()
 			/* Then merge node into node_2, and leave node_2
 			   in the worklist, to be processed on the next
 			   iteration.  */
-			add_edge (node, node_2, NULL);
+			add_edge (node, node_2, NULL, change);
 			continue;
 		      }
 		    else
@@ -2080,12 +2113,12 @@ exploded_graph::process_worklist ()
 			if (merged_enode == node)
 			  m_worklist.add_node (merged_enode);
 			else
-			  add_edge (node, merged_enode, NULL);
+			  add_edge (node, merged_enode, NULL, change);
 
 			if (merged_enode == node_2)
 			  m_worklist.add_node (merged_enode);
 			else
-			  add_edge (node_2, merged_enode, NULL);
+			  add_edge (node_2, merged_enode, NULL, change);
 
 			continue;
 		      }
@@ -2277,17 +2310,20 @@ exploded_graph::process_node (exploded_node *node)
     case PK_BEFORE_SUPERNODE:
       {
 	program_state next_state (state);
+	state_change change;
 
 	if (point.get_from_edge ())
 	  {
 	    impl_region_model_context ctxt (*this, node,
-					    &state, &next_state, NULL);
+					    &state, &next_state, &change,
+					    NULL);
 	    const cfg_superedge *last_cfg_superedge
 	      = point.get_from_edge ()->dyn_cast_cfg_superedge ();
 	    if (last_cfg_superedge)
 	      next_state.m_region_model->update_for_phis
 		(node->get_supernode (),
 		 last_cfg_superedge,
+		 &change,
 		 &ctxt);
 	  }
 
@@ -2299,7 +2335,7 @@ exploded_graph::process_node (exploded_node *node)
 	    exploded_node *next
 	      = get_or_create_node (next_point, next_state);
 	    if (next)
-	      add_edge (node, next, NULL);
+	      add_edge (node, next, NULL, change);
 	  }
 	else
 	  {
@@ -2308,7 +2344,7 @@ exploded_graph::process_node (exploded_node *node)
 						point.get_call_string ());
 	    exploded_node *next = get_or_create_node (next_point, next_state);
 	    if (next)
-	      add_edge (node, next, NULL);
+	      add_edge (node, next, NULL, change);
 	  }
       }
       break;
@@ -2330,6 +2366,7 @@ exploded_graph::process_node (exploded_node *node)
 	   Update next_state in-place, to get the result of the one
 	   or more stmts that are processed.  */
 	program_state next_state (state);
+	state_change change;
 	const supernode *snode = point.get_supernode ();
 	unsigned stmt_idx;
 	for (stmt_idx = point.get_stmt_idx ();
@@ -2347,7 +2384,7 @@ exploded_graph::process_node (exploded_node *node)
 
 	    /* Process the stmt.  */
 	    bool any_sm_changes
-	      = node->on_stmt (*this, snode, stmt, &next_state);
+	      = node->on_stmt (*this, snode, stmt, &next_state, &change);
 
 	    if (any_sm_changes || flag_analyzer_fine_grained)
 	      break;
@@ -2362,7 +2399,7 @@ exploded_graph::process_node (exploded_node *node)
 	exploded_node *next = get_or_create_node (next_point,
 						  next_state);
 	if (next)
-	  add_edge (node, next, NULL);
+	  add_edge (node, next, NULL, change);
       }
       break;
     case PK_AFTER_SUPERNODE:
@@ -2401,12 +2438,14 @@ exploded_graph::process_node (exploded_node *node)
 	    log ("considering SN: %i -> SN: %i",
 		 succ->m_src->m_index, succ->m_dest->m_index);
 
+	    state_change change;
+
 	    program_point next_point
 	      = program_point::before_supernode (succ->m_dest, succ,
 						 point.get_call_string ());
 	    program_state next_state (state);
 
-	    if (!node->on_edge (*this, succ, &next_point, &next_state))
+	    if (!node->on_edge (*this, succ, &next_point, &next_state, &change))
 	      {
 		log ("skipping impossible edge to SN: %i",
 		     succ->m_dest->m_index);
@@ -2415,7 +2454,7 @@ exploded_graph::process_node (exploded_node *node)
 
 	    exploded_node *next = get_or_create_node (next_point, next_state);
 	    if (next)
-	      add_edge (node, next, succ);
+	      add_edge (node, next, succ, change);
 	  }
       }
       break;
@@ -2665,7 +2704,7 @@ exploded_path::feasible_p (logger *logger) const
 		logger->log ("  update for phis");
 	      model.update_for_phis (src_enode.get_supernode (),
 				     last_cfg_superedge,
-				     NULL);
+				     NULL, NULL);
 	    }
 	}
 
@@ -3504,7 +3543,7 @@ impl_run_checkers (logger *logger)
       auto_client_timevar tv ("writing .eg.dot");
       char *filename
 	= concat (dump_base_name, ".eg.dot", NULL);
-      exploded_graph::dump_args_t args;
+      exploded_graph::dump_args_t args (eg);
       root_cluster c;
       eg.dump_dot (filename, &c, args);
       free (filename);
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index c5c33fb..de3ec4b 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -44,11 +44,13 @@ class impl_region_model_context : public region_model_context, public log_user
 				old state, rather than the new?  */
 			     const program_state *old_state,
 			     program_state *new_state,
+			     state_change *change,
 
 			     const gimple *stmt,
 			     stmt_finder *stmt_finder = NULL);
 
   impl_region_model_context (program_state *state,
+			     state_change *change,
 			     const extrinsic_state &ext_state);
 
   void warn (pending_diagnostic *d) FINAL OVERRIDE;
@@ -100,6 +102,7 @@ class impl_region_model_context : public region_model_context, public log_user
   const exploded_node *m_enode_for_diag;
   const program_state *m_old_state;
   program_state *m_new_state;
+  state_change *m_change;
   const gimple *m_stmt;
   stmt_finder *m_stmt_finder;
   const extrinsic_state &m_ext_state;
@@ -148,7 +151,11 @@ struct eg_traits
   typedef exploded_node node_t;
   typedef exploded_edge edge_t;
   typedef exploded_graph graph_t;
-  struct dump_args_t {};
+  struct dump_args_t
+  {
+    dump_args_t (const exploded_graph &eg) : m_eg (eg) {}
+    const exploded_graph &m_eg;
+  };
   typedef exploded_cluster cluster_t;
 };
 
@@ -181,11 +188,13 @@ class exploded_node : public dnode<eg_traits>
   bool on_stmt (exploded_graph &eg,
 		const supernode *snode,
 		const gimple *stmt,
-		program_state *state) const;
+		program_state *state,
+		state_change *change) const;
   bool on_edge (exploded_graph &eg,
 		const superedge *succ,
 		program_point *next_point,
-		program_state *next_state) const;
+		program_state *next_state,
+		state_change *change) const;
   void on_longjmp (exploded_graph &eg,
 		   const gcall *call,
 		   program_state *new_state,
@@ -271,7 +280,9 @@ class exploded_edge : public dedge<eg_traits>
 {
  public:
   exploded_edge (exploded_node *src, exploded_node *dest,
-		 const superedge *sedge, rewind_info_t *rewind_info);
+		 const superedge *sedge,
+		 const state_change &change,
+		 rewind_info_t *rewind_info);
   ~exploded_edge ();
   void dump_dot (graphviz_out *gv, const dump_args_t &args)
     const FINAL OVERRIDE;
@@ -279,6 +290,8 @@ class exploded_edge : public dedge<eg_traits>
   //private:
   const superedge *const m_sedge;
 
+  const state_change m_change;
+
   /* NULL for most edges; will be non-NULL for an unwind from a longjmp
      to a setjmp (owned by this class).  */
   rewind_info_t *m_rewind_info;
@@ -638,6 +651,7 @@ public:
 				     const program_state &state);
   void add_edge (exploded_node *src, exploded_node *dest,
 		 const superedge *sedge,
+		 const state_change &change,
 		 rewind_info_t *rewind_info = NULL);
 
   per_program_point_data *
diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h
index 8d5f5ac..f657edf 100644
--- a/gcc/analyzer/program-point.h
+++ b/gcc/analyzer/program-point.h
@@ -82,7 +82,12 @@ public:
     m_stmt_idx (stmt_idx), m_kind (kind)
   {
     if (from_edge)
-      gcc_assert (m_kind == PK_BEFORE_SUPERNODE);
+      {
+	gcc_assert (m_kind == PK_BEFORE_SUPERNODE);
+#if 0
+	gcc_assert (from_edge->get_kind () == SUPEREDGE_CFG_EDGE);
+#endif
+      }
     if (stmt_idx)
       gcc_assert (m_kind == PK_BEFORE_STMT);
   }
@@ -135,6 +140,10 @@ public:
   static function_point before_supernode (const supernode *supernode,
 					  const superedge *from_edge)
   {
+#if 0
+    if (from_edge && from_edge->get_kind () != SUPEREDGE_CFG_EDGE)
+      from_edge = NULL;
+#endif
     return function_point (supernode, from_edge, 0, PK_BEFORE_SUPERNODE);
   }
 
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 95ee0c3..364bfc7 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -653,7 +653,8 @@ program_state::dump (const extrinsic_state &ext_state,
 bool
 program_state::on_edge (exploded_graph &eg,
 			const exploded_node &enode,
-			const superedge *succ)
+			const superedge *succ,
+			state_change *change) // FIXME
 {
   /* Update state.  */
   const program_point &point = enode.get_point ();
@@ -670,7 +671,7 @@ program_state::on_edge (exploded_graph &eg,
 
   impl_region_model_context ctxt (eg, &enode,
 				  &enode.get_state (),
-				  this,
+				  this, change,
 				  last_stmt);
   if (!m_region_model->maybe_update_for_edge (*succ,
 					      last_stmt,
@@ -768,7 +769,9 @@ program_state::prune_for_point (exploded_graph &eg,
   // FIXME: which enode to use, if any?
   impl_region_model_context ctxt (eg, NULL,
 				  this,
-				  &new_state, NULL);
+				  &new_state,
+				  NULL, // FIXME: state_change
+				  NULL);
   new_state.m_region_model->purge_unused_svalues (&stats, &ctxt);
   eg.log ("num svalues purged: %i", stats.m_num_svalues);
   eg.log ("num regions purged: %i", stats.m_num_regions);
@@ -882,7 +885,7 @@ program_state::can_merge_with_p (const program_state &other,
 	}
     }
 
-  impl_region_model_context ctxt (out, ext_state);
+  impl_region_model_context ctxt (out, NULL, ext_state); // FIXME: state_change
   out->m_region_model->canonicalize (&ctxt);
 
   return true;
@@ -909,6 +912,86 @@ program_state::validate (const extrinsic_state &ext_state) const
     }
 }
 
+/* FIXME.  */
+
+void
+state_change::sm_change::dump (pretty_printer *pp,
+			       const extrinsic_state &ext_state) const
+{
+  const state_machine &sm = get_sm (ext_state);
+  pp_string (pp, "(");
+  m_old_sid.print (pp);
+  pp_string (pp, "/");
+  m_new_sid.print (pp);
+  pp_string (pp, "/origin: ");
+  m_old_origin_sid.print (pp);
+  pp_printf (pp, ": %s: %qs -> %qs)",
+	     sm.get_name (),
+	     sm.get_state_name (m_old_state),
+	     sm.get_state_name (m_new_state));
+}
+
+/* FIXME.  */
+
+state_change::state_change ()
+{
+}
+
+/* FIXME.  */
+
+state_change::state_change (const state_change &other)
+: m_sm_changes (other.m_sm_changes.length ())
+{
+  unsigned i;
+  sm_change *change;
+  FOR_EACH_VEC_ELT (other.m_sm_changes, i, change)
+    m_sm_changes.quick_push (*change);
+}
+
+/* FIXME.  */
+
+void
+state_change::add_sm_change (int sm_idx,
+			     svalue_id old_sid, svalue_id new_sid,
+			     svalue_id old_origin_sid,
+			     state_machine::state_t old_state,
+			     state_machine::state_t new_state)
+{
+  m_sm_changes.safe_push (sm_change (sm_idx,
+				     old_sid, new_sid,
+				     old_origin_sid,
+				     old_state, new_state));
+}
+
+/* FIXME.  */
+
+void
+state_change::dump (pretty_printer *pp,
+		    const extrinsic_state &ext_state) const
+{
+  unsigned i;
+  sm_change *change;
+  FOR_EACH_VEC_ELT (m_sm_changes, i, change)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      change->dump (pp, ext_state);
+    }
+}
+
+/* FIXME.  */
+
+void
+state_change::dump (const extrinsic_state &ext_state) const
+{
+  pretty_printer pp;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = stderr;
+  dump (&pp, ext_state);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
 #if CHECKING_P
 
 namespace selftest {
@@ -1061,7 +1144,7 @@ test_program_state_merging ()
   extrinsic_state ext_state (checkers);
 
   program_state s0 (ext_state);
-  impl_region_model_context ctxt (&s0, ext_state);
+  impl_region_model_context ctxt (&s0, NULL, ext_state);
 
   region_model *model0 = s0.m_region_model;
   region_id new_rid = model0->add_new_malloc_region ();
@@ -1097,7 +1180,7 @@ test_program_state_merging ()
 	     TEST_STATE);
 
   /* Try canonicalizing.  */
-  impl_region_model_context merged_ctxt (&merged, ext_state);
+  impl_region_model_context merged_ctxt (&merged, NULL, ext_state);
   merged.m_region_model->canonicalize (&merged_ctxt);
   merged.validate (ext_state);
 
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 14cfe56..27c129a 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -239,7 +239,8 @@ public:
 
   bool on_edge (exploded_graph &eg,
 		const exploded_node &enode,
-		const superedge *succ);
+		const superedge *succ,
+		state_change *change);
 
   program_state prune_for_point (exploded_graph &eg,
 				 const program_point &point) const;
@@ -276,4 +277,53 @@ public:
   auto_delete_vec<sm_state_map> m_checker_states;
 };
 
+/* FIXME.  */
+
+class state_change
+{
+ public:
+  struct sm_change
+  {
+    sm_change (int sm_idx,
+	       svalue_id old_sid, svalue_id new_sid,
+	       svalue_id old_origin_sid,
+	       state_machine::state_t old_state,
+	       state_machine::state_t new_state)
+    : m_sm_idx (sm_idx),
+      m_old_sid (old_sid), m_new_sid (new_sid),
+      m_old_origin_sid (old_origin_sid),
+      m_old_state (old_state), m_new_state (new_state)
+    {}
+
+    const state_machine &get_sm (const extrinsic_state &ext_state) const
+    {
+      return ext_state.get_sm (m_sm_idx);
+    }
+
+    void dump (pretty_printer *pp, const extrinsic_state &ext_state) const;
+
+    int m_sm_idx;
+    svalue_id m_old_sid;
+    svalue_id m_new_sid;
+    svalue_id m_old_origin_sid;
+    state_machine::state_t m_old_state;
+    state_machine::state_t m_new_state;
+  };
+
+  state_change ();
+  state_change (const state_change &other);
+
+  void add_sm_change (int sm_idx,
+		      svalue_id old_sid, svalue_id new_sid,
+		      svalue_id old_origin_sid,
+		      state_machine::state_t old_state,
+		      state_machine::state_t new_state);
+
+  void dump (pretty_printer *pp, const extrinsic_state &ext_state) const;
+  void dump (const extrinsic_state &ext_state) const;
+
+ private:
+  auto_vec<sm_change> m_sm_changes;
+};
+
 #endif /* GCC_ANALYZER_PROGRAM_STATE_H */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index cf2e7aa..2f7e521 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -5641,6 +5641,7 @@ region_model::set_to_new_unknown_value (region_id dst_rid, tree type,
 void
 region_model::update_for_phis (const supernode *snode,
 			       const cfg_superedge *last_cfg_superedge,
+			       state_change *change, // TODO
 			       region_model_context *ctxt)
 {
   gcc_assert (last_cfg_superedge);
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 3b12391..3751ac0 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -1672,6 +1672,7 @@ class region_model
 
   void update_for_phis (const supernode *snode,
 			const cfg_superedge *last_cfg_superedge,
+			state_change *change,
 			region_model_context *ctxt);
 
   void handle_phi (tree lhs, tree rhs, bool is_back_edge,
diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h
index 52f4734..0a89f8a 100644
--- a/gcc/analyzer/sm.h
+++ b/gcc/analyzer/sm.h
@@ -135,8 +135,10 @@ public:
   }
 
 protected:
-  sm_context (const state_machine &sm) : m_sm (sm) {}
+  sm_context (int sm_idx, const state_machine &sm)
+  : m_sm_idx (sm_idx), m_sm (sm) {}
 
+  int m_sm_idx;
   const state_machine &m_sm;
 };
 
diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc
index cc96c25..ab929b4 100644
--- a/gcc/analyzer/state-purge.cc
+++ b/gcc/analyzer/state-purge.cc
@@ -315,13 +315,14 @@ state_purge_per_ssa_name::process_point (const function_point &point,
 	/* Add given pred to worklist.  */
 	if (point.m_from_edge)
 	  {
-	    if (point.m_from_edge->m_src)
+	    if (/*(point.m_from_edge->get_kind () == SUPEREDGE_CFG_EDGE
+		 || (point.m_from_edge->get_kind ()
+		     == SUPEREDGE_INTRAPROCEDURAL_CALL))
+		     && */point.m_from_edge->m_src)
 	      add_to_worklist
 		(function_point::after_supernode (point.m_from_edge->m_src),
 		 worklist, logger);
 	  }
-	else
-	  gcc_assert (snode->entry_p ());
       }
       break;
 
-- 
1.8.5.3

