From f5a310e781b49e1fed2da01d3278c15c5df8e4ef Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 10 May 2019 21:01:19 -0400
Subject: [PATCH 44/54] FIXME: move exploded_graph::emit_diagnostic and
 prune_path to diagnostic_manager

---
 gcc/analyzer/impl-4.cc | 922 ++++++++++++++++++++++++-------------------------
 1 file changed, 454 insertions(+), 468 deletions(-)

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 43fe40b..a5b4e6d 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -904,9 +904,11 @@ public:
 
    Potentially could also do de-duplication of similar diagnostics.  */
 
-class diagnostic_manager
+class diagnostic_manager : public log_user
 {
 public:
+  diagnostic_manager (logger *logger, int verbosity);
+
   void add_diagnostic (const state_machine &sm,
 		       const exploded_node *enode,
 		       const supernode *node, const gimple *stmt,
@@ -919,8 +921,13 @@ public:
 			      const shortest_paths &sp,
 			      const saved_diagnostic &sd);
 
+  void prune_path (checker4_path *path,
+		   const state_machine &sm,
+		   tree var, state_machine::state_t state) const;
+
 private:
   auto_delete_vec<saved_diagnostic> m_saved_diagnostics;
+  const int m_verbosity;
 };
 
 /* An exploded_graph is a directed graph of unique <point, state> pairs.
@@ -959,18 +966,6 @@ public:
 			tree var, state_machine::state_t state,
 			pending_diagnostic *d);
 
-  void emit_diagnostic (const state_machine &sm,
-			const exploded_node *enode,
-			const supernode *node, const gimple *stmt,
-			stmt_finder *finder,
-			tree var, state_machine::state_t state,
-			pending_diagnostic *d,
-			const shortest_paths &sp) const;
-
-  void prune_path (checker4_path *path,
-		   const state_machine &sm,
-		   tree var, state_machine::state_t state) const;
-
   diagnostic_manager &get_diagnostic_manager ()
   {
     return m_diagnostic_manager;
@@ -1003,8 +998,6 @@ private:
 
   diagnostic_manager m_diagnostic_manager;
 
-  const int m_verbosity;
-
   /* Stats.  */
   stats m_global_stats;
   typedef hash_map<function *, stats *> function_stat_map_t;
@@ -3348,6 +3341,13 @@ saved_diagnostic::~saved_diagnostic ()
 
 /* FIXME.  */
 
+diagnostic_manager::diagnostic_manager (logger *logger, int verbosity)
+: log_user (logger), m_verbosity (verbosity)
+{
+}
+
+/* FIXME.  */
+
 void
 diagnostic_manager::add_diagnostic (const state_machine &sm,
 				    const exploded_node *enode,
@@ -3368,7 +3368,7 @@ diagnostic_manager::add_diagnostic (const state_machine &sm,
 void
 diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
 {
-  //JIT_LOG_SCOPE (get_logger ());
+  JIT_LOG_SCOPE (get_logger ());
   auto_client_timevar tv ("diagnostic_manager::emit_saved_diagnostics");
 
   if (m_saved_diagnostics.length () == 0)
@@ -3390,88 +3390,463 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
 					   const shortest_paths &sp,
 					   const saved_diagnostic &sd)
 {
-  eg.emit_diagnostic (sd.m_sm, sd.m_enode, sd.m_node, sd.m_stmt,
-		      sd.m_stmt_finder,
-		      sd.m_var, sd.m_state, sd.m_d,
-		      sp);
-}
+  /* Generate a path.  */
+  exploded_path epath (sp.get_shortest_path (sd.m_enode));
+  pretty_printer *pp = global_dc->printer->clone ();
 
-////////////////////////////////////////////////////////////////////////////
+  checker4_path emission_path;
+  // TODO: build path
+  for (unsigned i = 0; i < epath.m_edges.length (); i++)
+    {
+      const exploded_edge *eedge = epath.m_edges[i];
+      if (get_logger ())
+	log ("EN %i -> EN %i",
+	     eedge->m_src->m_index,
+	     eedge->m_dest->m_index);
+      const exploded_node *src_node = eedge->m_src;
+      const program_point &src_point = src_node->get_point ();
+      const exploded_node *dst_node = eedge->m_dest;
+      const program_point &dst_point = dst_node->get_point ();
+      const int src_stack_depth = src_point.get_stack_depth ();
+      const int dst_stack_depth = dst_point.get_stack_depth ();
+      switch (dst_point.m_kind)
+	{
+	default:
+	  break;
+	case PK_BEFORE_SUPERNODE:
+	  if (src_point.m_kind == PK_AFTER_SUPERNODE)
+	    {
+	      if (eedge->m_sedge)
+		{
+		  switch (eedge->m_sedge->m_kind)
+		    {
+		    case SUPEREDGE_CFG_EDGE:
+		    case SUPEREDGE_CALL:
+		    case SUPEREDGE_INTRAPROCEDURAL_CALL:
+		      {
+			const gimple *last_stmt
+			  = src_point.m_supernode->get_last_stmt ();
+			emission_path.add_event
+			  (new edge_event (eedge->m_sedge,
+					   (last_stmt
+					    ? last_stmt->location
+					    : UNKNOWN_LOCATION),
+					   src_point.m_supernode->m_fun->decl,
+					   src_stack_depth));
+		      }
+		      break;
+		    case SUPEREDGE_RETURN:
+		      {
+			const return_superedge *return_edge
+			  = eedge->m_sedge->dyn_cast_return_superedge ();
+			gcc_assert (return_edge);
+			// FIXME: use as_a<>
 
-/* exploded_graph's ctor.  */
+			const gcall *call_stmt = return_edge->get_call_stmt ();
+			emission_path.add_event
+			  (new edge_event (eedge->m_sedge,
+					   (call_stmt
+					    ? call_stmt->location
+					    : UNKNOWN_LOCATION),
+					   dst_point.m_supernode->m_fun->decl,
+					   dst_stack_depth));
+		      }
+		      break;
+		    }
+		}
+	    }
+	  // TODO: add function entry events
+	  if (dst_point.m_supernode->entry_p ())
+	    {
+	      emission_path.add_event
+		(new function_entry_event
+		 (dst_point.m_supernode->get_start_location (),
+		  dst_point.m_supernode->m_fun->decl,
+		  dst_stack_depth));
+	    }
+	  break;
+	case PK_BEFORE_STMT:
+	  emission_path.add_event
+	    (new statement_event (dst_point.get_stmt (),
+				  dst_point.m_supernode->m_fun->decl,
+				  dst_stack_depth));
+	  break;
+	case PK_AFTER_STMT:
+	  break;
+	}
 
-exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
-				const extrinsic_state &ext_state,
-				const state_purge_map *purge_map,
-				int verbosity)
-: log_user (logger), m_sg (sg), m_ext_state (ext_state),
-  m_purge_map (purge_map),
-  m_verbosity (verbosity),
-  m_global_stats (m_sg.num_nodes ()),
-  m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
-{
-  m_origin = get_or_create_node (program_point (NULL, NULL, 0, PK_ORIGIN,
-						call_string ()),
-				 program_state (ext_state));
-  for (int i = 0; i < m_sg.num_nodes (); i++)
-    m_PK_AFTER_SUPERNODE_per_snode.safe_push (i);
-}
+      /* Add state change events for the states that have changed.  */
+      const program_state &src_state = src_node->get_state ();
+      const program_state &dst_state = dst_node->get_state ();
+      const extrinsic_state &ext_state = eg.get_ext_state ();
+      gcc_assert (src_state.m_checker_states.length ()
+		  == ext_state.m_checkers.length ());
+      gcc_assert (dst_state.m_checker_states.length ()
+		  == ext_state.m_checkers.length ());
+      for (unsigned i = 0; i < ext_state.m_checkers.length (); i++)
+	{
+	  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];
+	  for (sm_state_map::iterator_t iter = dst_smap.begin ();
+	       iter != dst_smap.end ();
+	       ++iter)
+	    {
+	      path_var pv = (*iter).first;
+	      state_machine::state_t dst_sm_val = (*iter).second.m_state;
+	      state_machine::state_t src_sm_val = src_smap.get_state (pv);
+	      if (dst_sm_val != src_sm_val)
+		{
+		  const gimple *stmt = dst_point.get_stmt ();
+		  const supernode *supernode = dst_point.m_supernode;
+		  int stack_depth = dst_stack_depth;
 
-/* exploded_graph's dtor.  */
+		  if (eedge->m_sedge
+		      && eedge->m_sedge->m_kind == SUPEREDGE_CFG_EDGE)
+		    {
+		      supernode = src_point.m_supernode;
+		      stmt = supernode->get_last_stmt ();
+		      stack_depth = src_stack_depth;
+		    }
 
-exploded_graph::~exploded_graph ()
-{
-  for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
-       iter != m_per_function_stats.end ();
-       ++iter)
-    delete (*iter).second;
-}
+		  /* FIXME: bulletproofing for state changes at calls/returns;
+		     is there a better way? */
+		  if (!stmt)
+		    continue;
 
-/* Ensure that there is an exploded_node representing an external call to
-   FUN, adding it to the worklist if creating it.
+		  emission_path.add_event
+		    (new state_change_event (supernode,
+					     stmt,
+					     stack_depth,
+					     sm,
+					     pv.m_tree,
+					     src_sm_val,
+					     dst_sm_val,
+					     (*iter).second.m_origin.m_tree));
+		}
+	    }
+	}
+    }
 
-   Add an edge from the origin exploded_node to the function entrypoint
-   exploded_node.  */
+  /* Support deferring the choice of stmt until after an emission path has
+     been built, using an optional stmt_finder.  */
+  const gimple *stmt = sd.m_stmt;
+  if (stmt == NULL)
+    {
+      gcc_assert (sd.m_stmt_finder);
+      stmt = sd.m_stmt_finder->find_stmt (epath);
+    }
+  gcc_assert (stmt);
 
-void
-exploded_graph::add_function_entry (function *fun)
-{
-  program_point point = program_point::from_function_entry (m_sg, fun);
-  program_state state (m_ext_state);
+  prune_path (&emission_path, sd.m_sm, sd.m_var, sd.m_state);
+  emission_path.add_final_event (sd.m_sm, sd.m_enode, stmt,
+				 sd.m_var, sd.m_state, sd.m_d);
+  emission_path.prepare_for_emission (sd.m_d);
 
-  add_edge (m_origin, get_or_create_node (point, state), NULL);
+  gcc_rich_location rich_loc (stmt->location);
+  rich_loc.set_path (&emission_path);
+
+  auto_cfun sentinel (sd.m_node->m_fun);
+  sd.m_d->emit (&rich_loc);
+  delete pp;
 }
 
-/* Get or create an exploded_node for (POINT, STATE).
-   If a new node is created, it is added to the worklist.  */
+/* FIXME.  */
 
-exploded_node *
-exploded_graph::get_or_create_node (const program_point &point,
-				    const program_state &state)
+void
+diagnostic_manager::prune_path (checker4_path *path,
+				const state_machine &sm,
+				tree var,
+				state_machine::state_t state
+				  ATTRIBUTE_UNUSED) const
 {
   JIT_LOG_FUNC (get_logger ());
-  if (get_logger ())
-    {
-      format f (false);
-      log ("point: %s", point.to_str (f).c_str ());
-      log ("state: %s", state.to_str (m_ext_state).c_str ());
-    }
-
-  auto_cfun sentinel (point.get_function ());
+  path->maybe_log (get_logger (), "path");
 
-  state.validate ();
+  // TODO
+  /* TODO: go backwards, skipping state change events that aren't var
+     but update the pertinent var when state-copying occurs.  */
+  int idx = path->m_events.length () - 1;
+  while (idx >= 0 && idx < path->m_events.length ())
+    {
+      checker4_event *base_event = path->m_events[idx];
+      if (get_logger ())
+	log  ("considering event %i, with var: %qE, state: %qs",
+	      idx, var, sm.get_state_name (state));
+      switch (base_event->m_kind)
+	{
+	default:
+	  gcc_unreachable ();
 
-  /* Prune state to try to improve the chances of a cache hit,
-     avoiding generating redundant nodes.  */
-  program_state pruned_state = state.prune_for_point (*this, point);
+	case EK_DEBUG:
+	  if (m_verbosity < 2)
+	    {
+	      log ("filtering event %i: debug event", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
 
-  if (get_logger ())
-    log ("pruned_state: %s", pruned_state.to_str (m_ext_state).c_str ());
+	case EK_STMT:
+	  if (m_verbosity < 2)
+	    {
+	      log ("filtering event %i: statement event", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
 
-  stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
+	case EK_FUNCTION_ENTRY:
+	  if (m_verbosity < 1)
+	    {
+	      log ("filtering event %i: function entry", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
 
-  point_and_state ps (point, pruned_state);
-  if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
+	case EK_STATE_CHANGE:
+	  {
+	    state_change_event *state_change = (state_change_event *)base_event;
+	    if (state_change->m_var == var)
+	      {
+		if (state_change->m_origin)
+		  {
+		    log ("event %i: switching var of interest from %qE to %qE",
+			 idx, var, state_change->m_origin);
+		    var = state_change->m_origin;
+		  }
+	      }
+	    else if (m_verbosity < 2)
+	      {
+		log ("filtering event %i: state change to %qE unrelated to %qE",
+		     idx, state_change->m_var, var);
+		path->delete_event (idx);
+	      }
+	  }
+	  break;
+
+	case EK_EDGE:
+	  {
+	    edge_event *event = (edge_event *)base_event;
+#if 0
+	    inform (base_event->m_loc, "considering edge_event %i: %s",
+		    idx, base_event->m_desc);
+#endif
+	    switch (event->m_edge->m_kind)
+	      {
+	      case SUPEREDGE_CFG_EDGE:
+		{
+		  const cfg_superedge& cfg_superedge
+		    = event->get_cfg_superedge ();
+		  const supernode *dest = event->m_edge->m_dest;
+		  /* Do we have an SSA_NAME defined via a phi node in
+		     the dest CFG node?  */
+		  if (TREE_CODE (var) == SSA_NAME)
+		    if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
+		      {
+			if (const gphi *phi
+			    = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (var)))
+			  {
+			    /* Update var based on its phi node.  */
+			    tree old_var = var;
+			    var = cfg_superedge.get_phi_arg (phi);
+			    log ("updating from %qE to %qE based on phi node",
+				 old_var, var);
+			    if (get_logger ())
+			      {
+				pretty_printer pp;
+				pp_gimple_stmt_1 (&pp, (gimple *)phi, 0, (dump_flags_t)0);
+				// FIXME: const_cast!
+				log ("  phi: %s", pp_formatted_text (&pp));
+			      }
+			  }
+		      }
+
+		  /* TODO: is this edge significant to var?
+		     See if var can be in other states in the dest, but not
+		     in other states in the src?
+		     Must have multiple sibling edges.  */
+		  // TODO: for now, filter all intraprocedural edges:
+		  if (m_verbosity < 2)
+		    {
+		      log ("filtering event %i: CFG edge", idx);
+		      path->delete_event (idx);
+		    }
+		}
+		break;
+
+	      case SUPEREDGE_CALL:
+		{
+		  const callgraph_superedge& cg_superedge
+		    = event->get_callgraph_superedge ();
+		  tree caller_var
+		    = cg_superedge.map_expr_from_callee_to_caller (var);
+		  if (caller_var)
+		    var = caller_var;
+		}
+		break;
+	      case SUPEREDGE_RETURN:
+		// TODO: potentially update var/state based on return value,
+		// args etc
+		{
+		  const callgraph_superedge& cg_superedge
+		    = event->get_callgraph_superedge ();
+		  tree callee_var
+		    = cg_superedge.map_expr_from_caller_to_callee (var);
+		  if (callee_var)
+		    var = callee_var;
+		}
+		break;
+	      case SUPEREDGE_INTRAPROCEDURAL_CALL:
+		break;
+	      }
+	  }
+	  break;
+
+	case EK_WARNING:
+	  break;
+	}
+      idx--;
+    }
+
+  /* Second pass to remove redundant interprocedural information.  For
+     example, given:
+     (1)- calling "f2" from "f1"
+     (2)--- entry to "f2"
+     (3)--- calling "f3" from "f2"
+     (4)----- entry to "f3"
+     (5)--- returning to "f2" to "f3"
+     (6)- returning to "f1" to "f2"
+     with no other intervening events, then none of these events are
+     likely to be interesting to the user.
+
+     Prune [..., call, function-entry, return, ...] triples repeatedly
+     until nothing has changed.  For the example above, this would
+     remove events (3, 4, 5), and then remove events (1, 2, 6).  */
+  bool changed = false;
+  do
+    {
+      changed = false;
+      int idx = path->m_events.length () - 3;
+      while (idx >= 0 && idx < path->m_events.length ())
+	{
+	  if (path->m_events[idx]->is_call_p ()
+	      && path->m_events[idx + 1]->is_function_entry_p ()
+	      && path->m_events[idx + 2]->is_return_p ())
+	    {
+	      if (get_logger ())
+		{
+		  label_text desc (path->m_events[idx]->get_desc ());
+		  log ("filtering events %i-%i:"
+		       " irrelevant call/entry/return: %s",
+		       idx, idx + 2, desc.m_buffer);
+		  desc.maybe_free ();
+		}
+	      path->delete_event (idx + 2);
+	      path->delete_event (idx + 1);
+	      path->delete_event (idx);
+	      changed = true;
+	    }
+	  idx--;
+	}
+
+    }
+  while (changed);
+
+  /* Third pass.  If all we're left with is in one function, then
+     filter function entry events.  */
+  if (!path->interprocedural_p ())
+    {
+      // FIXME
+      int idx = path->m_events.length () - 1;
+      while (idx >= 0 && idx < path->m_events.length ())
+	{
+	  checker4_event *base_event = path->m_events[idx];
+	  if (base_event->m_kind == EK_FUNCTION_ENTRY)
+	    {
+	      log ("filtering event %i:"
+		   " function entry for purely intraprocedural path", idx);
+	      path->delete_event (idx);
+	    }
+	  idx--;
+	}
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* exploded_graph's ctor.  */
+
+exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
+				const extrinsic_state &ext_state,
+				const state_purge_map *purge_map,
+				int verbosity)
+: log_user (logger), m_sg (sg), m_ext_state (ext_state),
+  m_purge_map (purge_map),
+  m_diagnostic_manager (logger, verbosity),
+  m_global_stats (m_sg.num_nodes ()),
+  m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
+{
+  m_origin = get_or_create_node (program_point (NULL, NULL, 0, PK_ORIGIN,
+						call_string ()),
+				 program_state (ext_state));
+  for (int i = 0; i < m_sg.num_nodes (); i++)
+    m_PK_AFTER_SUPERNODE_per_snode.safe_push (i);
+}
+
+/* exploded_graph's dtor.  */
+
+exploded_graph::~exploded_graph ()
+{
+  for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
+       iter != m_per_function_stats.end ();
+       ++iter)
+    delete (*iter).second;
+}
+
+/* Ensure that there is an exploded_node representing an external call to
+   FUN, adding it to the worklist if creating it.
+
+   Add an edge from the origin exploded_node to the function entrypoint
+   exploded_node.  */
+
+void
+exploded_graph::add_function_entry (function *fun)
+{
+  program_point point = program_point::from_function_entry (m_sg, fun);
+  program_state state (m_ext_state);
+
+  add_edge (m_origin, get_or_create_node (point, state), NULL);
+}
+
+/* Get or create an exploded_node for (POINT, STATE).
+   If a new node is created, it is added to the worklist.  */
+
+exploded_node *
+exploded_graph::get_or_create_node (const program_point &point,
+				    const program_state &state)
+{
+  JIT_LOG_FUNC (get_logger ());
+  if (get_logger ())
+    {
+      format f (false);
+      log ("point: %s", point.to_str (f).c_str ());
+      log ("state: %s", state.to_str (m_ext_state).c_str ());
+    }
+
+  auto_cfun sentinel (point.get_function ());
+
+  state.validate ();
+
+  /* Prune state to try to improve the chances of a cache hit,
+     avoiding generating redundant nodes.  */
+  program_state pruned_state = state.prune_for_point (*this, point);
+
+  if (get_logger ())
+    log ("pruned_state: %s", pruned_state.to_str (m_ext_state).c_str ());
+
+  stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
+
+  point_and_state ps (point, pruned_state);
+  if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
     {
       /* An exploded_node for PS already exists.  */
       log ("reused EN: %i", (*slot)->m_index);
@@ -3695,395 +4070,6 @@ exploded_graph::process_node (exploded_node *node)
 
 /* FIXME.  */
 
-void
-exploded_graph::emit_diagnostic (const state_machine &sm,
-				 const exploded_node *enode,
-				 const supernode *node, const gimple *stmt,
-				 stmt_finder *stmt_finder,
-				 tree var, state_machine::state_t state,
-				 pending_diagnostic *d,
-				 const shortest_paths &sp) const
-{
-  /* TODO: generate a path.  */
-  exploded_path epath (sp.get_shortest_path (enode));
-  pretty_printer *pp = global_dc->printer->clone ();
-
-  checker4_path emission_path;
-  // TODO: build path
-  for (unsigned i = 0; i < epath.m_edges.length (); i++)
-    {
-      const exploded_edge *eedge = epath.m_edges[i];
-      if (get_logger ())
-	log ("EN %i -> EN %i",
-	     eedge->m_src->m_index,
-	     eedge->m_dest->m_index);
-      const exploded_node *src_node = eedge->m_src;
-      const program_point &src_point = src_node->get_point ();
-      const exploded_node *dst_node = eedge->m_dest;
-      const program_point &dst_point = dst_node->get_point ();
-      const int src_stack_depth = src_point.get_stack_depth ();
-      const int dst_stack_depth = dst_point.get_stack_depth ();
-      switch (dst_point.m_kind)
-	{
-	default:
-	  break;
-	case PK_BEFORE_SUPERNODE:
-	  if (src_point.m_kind == PK_AFTER_SUPERNODE)
-	    {
-	      if (eedge->m_sedge)
-		{
-		  switch (eedge->m_sedge->m_kind)
-		    {
-		    case SUPEREDGE_CFG_EDGE:
-		    case SUPEREDGE_CALL:
-		    case SUPEREDGE_INTRAPROCEDURAL_CALL:
-		      {
-			const gimple *last_stmt
-			  = src_point.m_supernode->get_last_stmt ();
-			emission_path.add_event
-			  (new edge_event (eedge->m_sedge,
-					   (last_stmt
-					    ? last_stmt->location
-					    : UNKNOWN_LOCATION),
-					   src_point.m_supernode->m_fun->decl,
-					   src_stack_depth));
-		      }
-		      break;
-		    case SUPEREDGE_RETURN:
-		      {
-			const return_superedge *return_edge
-			  = eedge->m_sedge->dyn_cast_return_superedge ();
-			gcc_assert (return_edge);
-			// FIXME: use as_a<>
-
-			const gcall *call_stmt = return_edge->get_call_stmt ();
-			emission_path.add_event
-			  (new edge_event (eedge->m_sedge,
-					   (call_stmt
-					    ? call_stmt->location
-					    : UNKNOWN_LOCATION),
-					   dst_point.m_supernode->m_fun->decl,
-					   dst_stack_depth));
-		      }
-		      break;
-		    }
-		}
-	    }
-	  // TODO: add function entry events
-	  if (dst_point.m_supernode->entry_p ())
-	    {
-	      emission_path.add_event
-		(new function_entry_event
-		 (dst_point.m_supernode->get_start_location (),
-		  dst_point.m_supernode->m_fun->decl,
-		  dst_stack_depth));
-	    }
-	  break;
-	case PK_BEFORE_STMT:
-	  emission_path.add_event
-	    (new statement_event (dst_point.get_stmt (),
-				  dst_point.m_supernode->m_fun->decl,
-				  dst_stack_depth));
-	  break;
-	case PK_AFTER_STMT:
-	  break;
-	}
-
-      /* Add state change events for the states that have changed.  */
-      const program_state &src_state = src_node->get_state ();
-      const program_state &dst_state = dst_node->get_state ();
-      const extrinsic_state &ext_state = m_ext_state;
-      gcc_assert (src_state.m_checker_states.length ()
-		  == ext_state.m_checkers.length ());
-      gcc_assert (dst_state.m_checker_states.length ()
-		  == ext_state.m_checkers.length ());
-      for (unsigned i = 0; i < ext_state.m_checkers.length (); i++)
-	{
-	  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];
-	  for (sm_state_map::iterator_t iter = dst_smap.begin ();
-	       iter != dst_smap.end ();
-	       ++iter)
-	    {
-	      path_var pv = (*iter).first;
-	      state_machine::state_t dst_sm_val = (*iter).second.m_state;
-	      state_machine::state_t src_sm_val = src_smap.get_state (pv);
-	      if (dst_sm_val != src_sm_val)
-		{
-		  const gimple *stmt = dst_point.get_stmt ();
-		  const supernode *supernode = dst_point.m_supernode;
-		  int stack_depth = dst_stack_depth;
-
-		  if (eedge->m_sedge
-		      && eedge->m_sedge->m_kind == SUPEREDGE_CFG_EDGE)
-		    {
-		      supernode = src_point.m_supernode;
-		      stmt = supernode->get_last_stmt ();
-		      stack_depth = src_stack_depth;
-		    }
-
-		  /* FIXME: bulletproofing for state changes at calls/returns;
-		     is there a better way? */
-		  if (!stmt)
-		    continue;
-
-		  emission_path.add_event
-		    (new state_change_event (supernode,
-					     stmt,
-					     stack_depth,
-					     sm,
-					     pv.m_tree,
-					     src_sm_val,
-					     dst_sm_val,
-					     (*iter).second.m_origin.m_tree));
-		}
-	    }
-	}
-    }
-
-  /* Support deferring the choice of stmt until after an emission path has
-     been built, using an optional stmt_finder.  */
-  if (stmt == NULL)
-    {
-      gcc_assert (stmt_finder);
-      stmt = stmt_finder->find_stmt (epath);
-      gcc_assert (stmt);
-    }
-
-  prune_path (&emission_path, sm, var, state);
-  emission_path.add_final_event (sm, enode, stmt, var, state, d);
-  emission_path.prepare_for_emission (d);
-
-  gcc_rich_location rich_loc (stmt->location);
-  rich_loc.set_path (&emission_path);
-
-  auto_cfun sentinel (node->m_fun);
-  d->emit (&rich_loc);
-  delete pp;
-}
-
-/* FIXME.  */
-
-void
-exploded_graph::prune_path (checker4_path *path,
-			    const state_machine &sm,
-			    tree var,
-			    state_machine::state_t state ATTRIBUTE_UNUSED) const
-{
-  JIT_LOG_FUNC (get_logger ());
-  path->maybe_log (get_logger (), "path");
-
-  // TODO
-  /* TODO: go backwards, skipping state change events that aren't var
-     but update the pertinent var when state-copying occurs.  */
-  int idx = path->m_events.length () - 1;
-  while (idx >= 0 && idx < path->m_events.length ())
-    {
-      checker4_event *base_event = path->m_events[idx];
-      if (get_logger ())
-	log  ("considering event %i, with var: %qE, state: %qs",
-	      idx, var, sm.get_state_name (state));
-      switch (base_event->m_kind)
-	{
-	default:
-	  gcc_unreachable ();
-
-	case EK_DEBUG:
-	  if (m_verbosity < 2)
-	    {
-	      log ("filtering event %i: debug event", idx);
-	      path->delete_event (idx);
-	    }
-	  break;
-
-	case EK_STMT:
-	  if (m_verbosity < 2)
-	    {
-	      log ("filtering event %i: statement event", idx);
-	      path->delete_event (idx);
-	    }
-	  break;
-
-	case EK_FUNCTION_ENTRY:
-	  if (m_verbosity < 1)
-	    {
-	      log ("filtering event %i: function entry", idx);
-	      path->delete_event (idx);
-	    }
-	  break;
-
-	case EK_STATE_CHANGE:
-	  {
-	    state_change_event *state_change = (state_change_event *)base_event;
-	    if (state_change->m_var == var)
-	      {
-		if (state_change->m_origin)
-		  {
-		    log ("event %i: switching var of interest from %qE to %qE",
-			 idx, var, state_change->m_origin);
-		    var = state_change->m_origin;
-		  }
-	      }
-	    else if (m_verbosity < 2)
-	      {
-		log ("filtering event %i: state change to %qE unrelated to %qE",
-		     idx, state_change->m_var, var);
-		path->delete_event (idx);
-	      }
-	  }
-	  break;
-
-	case EK_EDGE:
-	  {
-	    edge_event *event = (edge_event *)base_event;
-#if 0
-	    inform (base_event->m_loc, "considering edge_event %i: %s",
-		    idx, base_event->m_desc);
-#endif
-	    switch (event->m_edge->m_kind)
-	      {
-	      case SUPEREDGE_CFG_EDGE:
-		{
-		  const cfg_superedge& cfg_superedge
-		    = event->get_cfg_superedge ();
-		  const supernode *dest = event->m_edge->m_dest;
-		  /* Do we have an SSA_NAME defined via a phi node in
-		     the dest CFG node?  */
-		  if (TREE_CODE (var) == SSA_NAME)
-		    if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
-		      {
-			if (const gphi *phi
-			    = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (var)))
-			  {
-			    /* Update var based on its phi node.  */
-			    tree old_var = var;
-			    var = cfg_superedge.get_phi_arg (phi);
-			    log ("updating from %qE to %qE based on phi node",
-				 old_var, var);
-			    if (get_logger ())
-			      {
-				pretty_printer pp;
-				pp_gimple_stmt_1 (&pp, (gimple *)phi, 0, (dump_flags_t)0);
-				// FIXME: const_cast!
-				log ("  phi: %s", pp_formatted_text (&pp));
-			      }
-			  }
-		      }
-
-		  /* TODO: is this edge significant to var?
-		     See if var can be in other states in the dest, but not
-		     in other states in the src?
-		     Must have multiple sibling edges.  */
-		  // TODO: for now, filter all intraprocedural edges:
-		  if (m_verbosity < 2)
-		    {
-		      log ("filtering event %i: CFG edge", idx);
-		      path->delete_event (idx);
-		    }
-		}
-		break;
-
-	      case SUPEREDGE_CALL:
-		{
-		  const callgraph_superedge& cg_superedge
-		    = event->get_callgraph_superedge ();
-		  tree caller_var
-		    = cg_superedge.map_expr_from_callee_to_caller (var);
-		  if (caller_var)
-		    var = caller_var;
-		}
-		break;
-	      case SUPEREDGE_RETURN:
-		// TODO: potentially update var/state based on return value,
-		// args etc
-		{
-		  const callgraph_superedge& cg_superedge
-		    = event->get_callgraph_superedge ();
-		  tree callee_var
-		    = cg_superedge.map_expr_from_caller_to_callee (var);
-		  if (callee_var)
-		    var = callee_var;
-		}
-		break;
-	      case SUPEREDGE_INTRAPROCEDURAL_CALL:
-		break;
-	      }
-	  }
-	  break;
-
-	case EK_WARNING:
-	  break;
-	}
-      idx--;
-    }
-
-  /* Second pass to remove redundant interprocedural information.  For
-     example, given:
-     (1)- calling "f2" from "f1"
-     (2)--- entry to "f2"
-     (3)--- calling "f3" from "f2"
-     (4)----- entry to "f3"
-     (5)--- returning to "f2" to "f3"
-     (6)- returning to "f1" to "f2"
-     with no other intervening events, then none of these events are
-     likely to be interesting to the user.
-
-     Prune [..., call, function-entry, return, ...] triples repeatedly
-     until nothing has changed.  For the example above, this would
-     remove events (3, 4, 5), and then remove events (1, 2, 6).  */
-  bool changed = false;
-  do
-    {
-      changed = false;
-      int idx = path->m_events.length () - 3;
-      while (idx >= 0 && idx < path->m_events.length ())
-	{
-	  if (path->m_events[idx]->is_call_p ()
-	      && path->m_events[idx + 1]->is_function_entry_p ()
-	      && path->m_events[idx + 2]->is_return_p ())
-	    {
-	      if (get_logger ())
-		{
-		  label_text desc (path->m_events[idx]->get_desc ());
-		  log ("filtering events %i-%i:"
-		       " irrelevant call/entry/return: %s",
-		       idx, idx + 2, desc.m_buffer);
-		  desc.maybe_free ();
-		}
-	      path->delete_event (idx + 2);
-	      path->delete_event (idx + 1);
-	      path->delete_event (idx);
-	      changed = true;
-	    }
-	  idx--;
-	}
-
-    }
-  while (changed);
-
-  /* Third pass.  If all we're left with is in one function, then
-     filter function entry events.  */
-  if (!path->interprocedural_p ())
-    {
-      // FIXME
-      int idx = path->m_events.length () - 1;
-      while (idx >= 0 && idx < path->m_events.length ())
-	{
-	  checker4_event *base_event = path->m_events[idx];
-	  if (base_event->m_kind == EK_FUNCTION_ENTRY)
-	    {
-	      log ("filtering event %i:"
-		   " function entry for purely intraprocedural path", idx);
-	      path->delete_event (idx);
-	    }
-	  idx--;
-	}
-    }
-}
-
-/* FIXME.  */
-
 stats *
 exploded_graph::get_or_create_function_stats (function *fn)
 {
-- 
1.8.5.3

