From f33c884e5ce059627d05fcd3b357717de86036a8 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 20 May 2020 14:56:10 -0400
Subject: [PATCH 162/315] FIXME: WIP on introducing state_change2_event to fix
 'first free' messages

---
 cover-letter.txt                   |   3 +
 gcc/analyzer/analyzer.h            |   2 +-
 gcc/analyzer/checker-path.cc       | 118 ++++++++-
 gcc/analyzer/checker-path.h        |  71 +++++-
 gcc/analyzer/diagnostic-manager.cc | 377 ++++++++++++++++++++++++++---
 gcc/analyzer/diagnostic-manager.h  |  21 +-
 gcc/analyzer/engine.cc             |  12 +-
 gcc/analyzer/pending-diagnostic.h  |   4 +-
 gcc/analyzer/program-state.h       |   2 +-
 gcc/analyzer/region-model2.cc      |  11 +
 gcc/analyzer/region-model2.h       |   1 +
 gcc/analyzer/sm-signal.cc          |   3 +-
 12 files changed, 576 insertions(+), 49 deletions(-)

diff --git a/cover-letter.txt b/cover-letter.txt
index 19c85309a90..80002b84087 100644
--- a/cover-letter.txt
+++ b/cover-letter.txt
@@ -72,3 +72,6 @@ TODO:
   - some selftests depend on bit sizes of types, hopefully via the TYPE_SIZE
      macros (see e.g. INT_TYPE_SIZE in defaults.h).  Remember to test on
      some other targets.  Similarly, big-endian vs little-endian
+  - loops
+  - phis
+  - path event descriptions
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 95bae455778..b40d8d5c876 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -82,7 +82,7 @@ class constraint_manager2;
 class equiv_class2;
 
 class pending_diagnostic;
-class state_change_event;
+class base_state_change_event;
 class checker_path;
 class extrinsic_state;
 class sm_state_map;
diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc
index c07273460d5..17a07244f2f 100644
--- a/gcc/analyzer/checker-path.cc
+++ b/gcc/analyzer/checker-path.cc
@@ -83,6 +83,8 @@ event_kind_to_string (enum event_kind ek)
       return "EK_FUNCTION_ENTRY";
     case EK_STATE_CHANGE:
       return "EK_STATE_CHANGE";
+    case EK_STATE_CHANGE2:
+      return "EK_STATE_CHANGE2";
     case EK_START_CFG_EDGE:
       return "EK_START_CFG_EDGE";
     case EK_END_CFG_EDGE:
@@ -219,9 +221,9 @@ state_change_event::state_change_event (const supernode *node,
 					state_machine::state_t to,
 					tree origin,
 					const program_state &dst_state)
-: checker_event (EK_STATE_CHANGE,
-		 stmt->location, node->m_fun->decl,
-		 stack_depth),
+: base_state_change_event (EK_STATE_CHANGE,
+			   stmt->location, node->m_fun->decl,
+			   stack_depth),
   m_node (node), m_stmt (stmt), m_sm (sm),
   m_var (var), m_from (from), m_to (to),
   m_origin (origin),
@@ -310,6 +312,116 @@ state_change_event::get_desc (bool can_colorize) const
     }
 }
 
+/* class state_change2_event : public checker_event.  */
+
+/* state_change2_event's ctor.  */
+
+state_change2_event::state_change2_event (const supernode *node,
+					  const gimple *stmt,
+					  int stack_depth,
+					  const state_machine &sm,
+					  const svalue2 *sval,
+					  state_machine::state_t from,
+					  state_machine::state_t to,
+					  const svalue2 *origin,
+					  const program_state &dst_state)
+: base_state_change_event (EK_STATE_CHANGE2,
+			   stmt->location, node->m_fun->decl,
+			   stack_depth),
+  m_node (node), m_stmt (stmt), m_sm (sm),
+  m_sval (sval), m_from (from), m_to (to),
+  m_origin (origin),
+  m_dst_state (dst_state)
+{
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+   state_change2_event.
+
+   Attempt to generate a nicer human-readable description.
+   For greatest precision-of-wording, give the pending diagnostic
+   a chance to describe this state change (in terms of the
+   diagnostic).
+   Note that we only have a pending_diagnostic set on the event once
+   the diagnostic is about to being emitted, so the description for
+   an event can change.  */
+
+label_text
+state_change2_event::get_desc (bool can_colorize) const
+{
+  if (m_pending_diagnostic)
+    {
+      tree var = NULL; // FIXME
+      tree origin = NULL; // FIXME
+      label_text custom_desc
+	= m_pending_diagnostic->describe_state_change
+	(evdesc::state_change (can_colorize, var, origin,
+			       m_from, m_to, m_emission_id, *this));
+      if (custom_desc.m_buffer)
+	{
+	  if (flag_analyzer_verbose_state_changes)
+	    {
+	      /* Append debug version.  */
+	      label_text result;
+	      if (m_origin)
+		result = make_label_text
+		  (can_colorize,
+		   "%s (state of %qE: %qs -> %qs, origin: %qE)",
+		   custom_desc.m_buffer,
+		   var,
+		   m_sm.get_state_name (m_from),
+		   m_sm.get_state_name (m_to),
+		   origin);
+	      else
+		result = make_label_text
+		  (can_colorize,
+		   "%s (state of %qE: %qs -> %qs, NULL origin)",
+		   custom_desc.m_buffer,
+		   var,
+		   m_sm.get_state_name (m_from),
+		   m_sm.get_state_name (m_to));
+	      custom_desc.maybe_free ();
+	      return result;
+	    }
+	  else
+	    return custom_desc;
+	}
+    }
+
+  /* Fallback description.  */
+  if (m_sval)
+    {
+      label_text sval_desc = m_sval->get_desc ();
+      if (m_origin)
+	{
+	  label_text origin_desc = m_origin->get_desc ();
+	  return make_label_text
+	    (can_colorize,
+	     "state of %qs: %qs -> %qs (origin: %qs)",
+	     sval_desc.m_buffer,
+	     m_sm.get_state_name (m_from),
+	     m_sm.get_state_name (m_to),
+	     origin_desc.m_buffer);
+	}
+      else
+	return make_label_text
+	  (can_colorize,
+	   "state of %qs: %qs -> %qs (NULL origin)",
+	   sval_desc.m_buffer,
+	   m_sm.get_state_name (m_from),
+	   m_sm.get_state_name (m_to));
+    }
+  else
+    {
+      gcc_assert (m_origin == NULL);
+      return make_label_text
+	(can_colorize,
+	 "global state: %qs -> %qs",
+	 m_sm.get_state_name (m_from),
+	 m_sm.get_state_name (m_to));
+    }
+}
+
 /* class superedge_event : public checker_event.  */
 
 /* Get the callgraph_superedge for this superedge_event, which must be
diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h
index 72c8444c743..46b398729f7 100644
--- a/gcc/analyzer/checker-path.h
+++ b/gcc/analyzer/checker-path.h
@@ -35,6 +35,7 @@ enum event_kind
   EK_STMT,
   EK_FUNCTION_ENTRY,
   EK_STATE_CHANGE,
+  EK_STATE_CHANGE2,
   EK_START_CFG_EDGE,
   EK_END_CFG_EDGE,
   EK_CALL_EDGE,
@@ -58,7 +59,9 @@ extern const char *event_kind_to_string (enum event_kind ek);
        custom_event (EK_CUSTOM)
        statement_event (EK_STMT)
        function_entry_event (EK_FUNCTION_ENTRY)
-       state_change_event (EK_STATE_CHANGE)
+       base_state_change_event
+	 state_change_event (EK_STATE_CHANGE)
+	 state_change2_event (EK_STATE_CHANGE2)
        superedge_event
          cfg_edge_event
 	   start_cfg_edge_event (EK_START_CFG_EDGE)
@@ -187,9 +190,23 @@ public:
   bool is_function_entry_p () const FINAL OVERRIDE { return true; }
 };
 
+// FIXME: eliminate this once we get rid of the old implementation
+
+class base_state_change_event : public checker_event
+{
+public:
+  virtual function *get_dest_function () const = 0;
+
+protected:
+    base_state_change_event (enum event_kind kind,
+			   location_t loc, tree fndecl, int depth)
+  : checker_event (kind, loc, fndecl, depth)
+  {}
+};
+
 /* Subclass of checker_event describing a state change.  */
 
-class state_change_event : public checker_event
+class state_change_event : public base_state_change_event
 {
 public:
   state_change_event (const supernode *node, const gimple *stmt,
@@ -215,6 +232,11 @@ public:
     return m_dst_state.m_region_model2->get_lvalue (expr, ctxt);
   }
 
+  function *get_dest_function () const
+  {
+    return m_dst_state.get_current_function ();
+  }
+
   const supernode *m_node;
   const gimple *m_stmt;
   const state_machine &m_sm;
@@ -225,6 +247,51 @@ public:
   program_state m_dst_state;
 };
 
+/* Subclass of checker_event describing a state change.  */
+
+class state_change2_event : public base_state_change_event
+{
+public:
+  state_change2_event (const supernode *node, const gimple *stmt,
+		       int stack_depth,
+		       const state_machine &sm,
+		       const svalue2 *sval,
+		       state_machine::state_t from,
+		       state_machine::state_t to,
+		       const svalue2 *origin,
+		       const program_state &dst_state);
+
+  label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
+
+#if 0
+  region_id get_lvalue (tree expr, region_model_context *ctxt) const
+  {
+    gcc_assert (m_dst_state.m_region_model);
+    return m_dst_state.m_region_model->get_lvalue (expr, ctxt);
+  }
+
+  const region2* get_lvalue2 (tree expr, region_model2_context *ctxt) const
+  {
+    gcc_assert (m_dst_state.m_region_model2);
+    return m_dst_state.m_region_model2->get_lvalue (expr, ctxt);
+  }
+#endif
+
+  function *get_dest_function () const
+  {
+    return m_dst_state.get_current_function ();
+  }
+
+  const supernode *m_node;
+  const gimple *m_stmt;
+  const state_machine &m_sm;
+  const svalue2 *m_sval;
+  state_machine::state_t m_from;
+  state_machine::state_t m_to;
+  const svalue2 *m_origin;
+  program_state m_dst_state;
+};
+
 /* Subclass of checker_event; parent class for subclasses that relate to
    a superedge.  */
 
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 85234d4f133..ded5dd2cae3 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -71,13 +71,15 @@ saved_diagnostic::saved_diagnostic (const state_machine *sm,
 				    const exploded_node *enode,
 				    const supernode *snode, const gimple *stmt,
 				    stmt_finder *stmt_finder,
-				    tree var, state_machine::state_t state,
+				    tree var,
+				    const svalue2 *sval,
+				    state_machine::state_t state,
 				    pending_diagnostic *d)
 : m_sm (sm), m_enode (enode), m_snode (snode), m_stmt (stmt),
  /* stmt_finder could be on-stack; we want our own copy that can
     outlive that.  */
   m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
-  m_var (var), m_state (state),
+  m_var (var), m_sval (sval), m_state (state),
   m_d (d), m_trailing_eedge (NULL),
   m_status (STATUS_NEW), m_epath_length (0), m_problem (NULL)
 {
@@ -106,6 +108,7 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const
 	  && m_stmt == other.m_stmt
 	  /* We don't compare m_stmt_finder.  */
 	  && pending_diagnostic::same_tree_p (m_var, other.m_var)
+	  && m_sval == other.m_sval
 	  && m_state == other.m_state
 	  && m_d->equal_p (*other.m_d)
 	  && m_trailing_eedge == other.m_trailing_eedge);
@@ -150,8 +153,9 @@ private:
 
 /* diagnostic_manager's ctor.  */
 
-diagnostic_manager::diagnostic_manager (logger *logger, int verbosity)
-: log_user (logger), m_verbosity (verbosity)
+diagnostic_manager::diagnostic_manager (logger *logger, engine2 *eng,
+					int verbosity)
+: log_user (logger), m_eng (eng), m_verbosity (verbosity)
 {
 }
 
@@ -162,7 +166,9 @@ diagnostic_manager::add_diagnostic (const state_machine *sm,
 				    const exploded_node *enode,
 				    const supernode *snode, const gimple *stmt,
 				    stmt_finder *finder,
-				    tree var, state_machine::state_t state,
+				    tree var,
+				    const svalue2 *sval,
+				    state_machine::state_t state,
 				    pending_diagnostic *d)
 {
   LOG_FUNC (get_logger ());
@@ -172,7 +178,8 @@ diagnostic_manager::add_diagnostic (const state_machine *sm,
   gcc_assert (enode);
 
   saved_diagnostic *sd
-    = new saved_diagnostic (sm, enode, snode, stmt, finder, var, state, d);
+    = new saved_diagnostic (sm, enode, snode, stmt, finder, var, sval,
+			    state, d);
   m_saved_diagnostics.safe_push (sd);
   if (get_logger ())
     log ("adding saved diagnostic %i at SN %i: %qs",
@@ -189,7 +196,7 @@ diagnostic_manager::add_diagnostic (const exploded_node *enode,
 				    pending_diagnostic *d)
 {
   gcc_assert (enode);
-  add_diagnostic (NULL, enode, snode, stmt, finder, NULL_TREE, 0, d);
+  add_diagnostic (NULL, enode, snode, stmt, finder, NULL_TREE, NULL, 0, d);
 }
 
 /* A class for identifying sets of duplicated pending_diagnostic.
@@ -547,7 +554,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
   build_emission_path (pb, epath, &emission_path);
 
   /* Now prune it to just cover the most pertinent events.  */
-  prune_path (&emission_path, sd.m_sm, sd.m_var, sd.m_state);
+  prune_path (&emission_path, sd.m_sm, sd.m_var, sd.m_sval, sd.m_state);
 
   /* Add a final event to the path, covering the diagnostic itself.
      We use the final enode from the epath, which might be different from
@@ -633,6 +640,16 @@ get_any_origin (const gimple *stmt,
   return NULL_TREE;
 }
 
+/* FIXME.  */
+
+static const svalue2 *
+get_any_origin2 (const gimple *stmt,
+		 const svalue2 *dst_sval,
+		 const program_state &dst_state)
+{
+  gcc_unreachable ();
+}
+
 /* Emit a "path" of events to EMISSION_PATH describing the exploded path
    EPATH within EG.  */
 
@@ -848,22 +865,22 @@ public:
 
     int stack_depth = src_stack_depth;
 
-    m_emission_path->add_event (new state_change_event (supernode,
-							stmt,
-							stack_depth,
-							sm,
-							NULL_TREE,
-							src_sm_val,
-							dst_sm_val,
-							NULL_TREE,
-							dst_state));
+    m_emission_path->add_event (new state_change2_event (supernode,
+							 stmt,
+							 stack_depth,
+							 sm,
+							 NULL,
+							 src_sm_val,
+							 dst_sm_val,
+							 NULL,
+							 dst_state));
     return false;
   }
 
   bool on_state_change (const state_machine &sm,
 			state_machine::state_t src_sm_val,
 			state_machine::state_t dst_sm_val,
-			tree dst_rep,
+			const svalue2 *sval,
 			const svalue2 *dst_origin_sval) FINAL OVERRIDE
   {
     const exploded_node *src_node = m_eedge.m_src;
@@ -895,15 +912,15 @@ public:
     if (origin_rep == NULL_TREE)
       origin_rep = get_any_origin (stmt, dst_rep, dst_state);
 #endif
-    m_emission_path->add_event (new state_change_event (supernode,
-							stmt,
-							stack_depth,
-							sm,
-							dst_rep,
-							src_sm_val,
-							dst_sm_val,
-							origin_rep,
-							dst_state));
+    m_emission_path->add_event (new state_change2_event (supernode,
+							 stmt,
+							 stack_depth,
+							 sm,
+							 sval,
+							 src_sm_val,
+							 dst_sm_val,
+							 dst_origin_sval,
+							 dst_state));
     return false;
   }
 
@@ -967,7 +984,7 @@ for_each_state_change2 (const program_state &src_state,
 	    {
 	      const svalue2 *origin_sval = (*iter).second.m_origin;
 	      if (visitor->on_state_change (sm, src_sm_val, dst_sm_val,
-					    dst_rep, origin_sval))
+					    sval, origin_sval))
 		return true;
 	    }
 #else
@@ -1283,11 +1300,15 @@ void
 diagnostic_manager::prune_path (checker_path *path,
 				const state_machine *sm,
 				tree var,
+				const svalue2 *sval,
 				state_machine::state_t state) const
 {
   LOG_FUNC (get_logger ());
   path->maybe_log (get_logger (), "path");
-  prune_for_sm_diagnostic (path, sm, var, state);
+  if (get_engine2 ())
+    prune_for_sm_diagnostic2 (path, sm, sval, state);
+  else
+    prune_for_sm_diagnostic (path, sm, var, state);
   prune_interproc_events (path);
   finish_pruning (path);
   path->maybe_log (get_logger (), "pruned");
@@ -1312,6 +1333,7 @@ can_be_expr_of_interest_p (tree expr)
   return true;
 }
 
+// FIXME: old implementation:
 /* First pass of diagnostic_manager::prune_path: apply verbosity level,
    pruning unrelated state change events.
 
@@ -1464,12 +1486,302 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 	  }
 	  break;
 
+	case EK_STATE_CHANGE2:
+	  /* Old implementation should create EK_STATE_CHANGE instead.  */
+	  gcc_unreachable ();
+
+	case EK_START_CFG_EDGE:
+	  {
+	    cfg_edge_event *event = (cfg_edge_event *)base_event;
+	    const cfg_superedge& cfg_superedge
+	      = event->get_cfg_superedge ();
+	    const supernode *dest = event->m_sedge->m_dest;
+	    /* Do we have an SSA_NAME defined via a phi node in
+	       the dest CFG node?  */
+	    if (var && TREE_CODE (var) == SSA_NAME)
+	      if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
+		{
+		  if (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, phi, 0, (dump_flags_t)0);
+			  log ("  phi: %s", pp_formatted_text (&pp));
+			}
+		      /* If we've chosen a bad exploded_path, then the
+			 phi arg might be a constant.  Fail gracefully for
+			 this case.  */
+		      update_for_unsuitable_sm_exprs (&var);
+		    }
+		}
+
+	    /* 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.  */
+
+	    if (event->should_filter_p (m_verbosity))
+	      {
+		log ("filtering event %i: CFG edge", idx);
+		path->delete_event (idx);
+		/* Also delete the corresponding EK_END_CFG_EDGE.  */
+		gcc_assert (path->get_checker_event (idx)->m_kind
+			    == EK_END_CFG_EDGE);
+		path->delete_event (idx);
+	      }
+	  }
+	  break;
+
+	case EK_END_CFG_EDGE:
+	  /* These come in pairs with EK_START_CFG_EDGE events and are
+	     filtered when their start event is filtered.  */
+	  break;
+
+	case EK_CALL_EDGE:
+	  {
+	    call_event *event = (call_event *)base_event;
+	    const callgraph_superedge& cg_superedge
+	      = event->get_callgraph_superedge ();
+	    callsite_expr expr;
+	    tree caller_var
+	      = cg_superedge.map_expr_from_callee_to_caller (var, &expr);
+	    if (caller_var)
+	      {
+		log ("event %i:"
+		     " switching var of interest"
+		     " from %qE in callee to %qE in caller",
+		     idx, var, caller_var);
+		var = caller_var;
+		if (expr.param_p ())
+		  event->record_critical_state (var, state);
+		update_for_unsuitable_sm_exprs (&var);
+	      }
+	  }
+	  break;
+
+	case EK_RETURN_EDGE:
+	  // TODO: potentially update var/state based on return value,
+	  // args etc
+	  {
+	    if (var)
+	      {
+		return_event *event = (return_event *)base_event;
+		const callgraph_superedge& cg_superedge
+		  = event->get_callgraph_superedge ();
+		callsite_expr expr;
+		tree callee_var
+		  = cg_superedge.map_expr_from_caller_to_callee (var, &expr);
+		if (callee_var)
+		  {
+		    log ("event %i:"
+			 " switching var of interest"
+			 " from %qE in caller to %qE in callee",
+			 idx, var, callee_var);
+		    var = callee_var;
+		    if (expr.return_value_p ())
+		      event->record_critical_state (var, state);
+		    update_for_unsuitable_sm_exprs (&var);
+		  }
+	      }
+	  }
+	  break;
+
+	case EK_SETJMP:
+	  /* TODO: only show setjmp_events that matter i.e. those for which
+	     there is a later rewind event using them.  */
+	case EK_REWIND_FROM_LONGJMP:
+	case EK_REWIND_TO_SETJMP:
+	  break;
+
+	case EK_WARNING:
+	  /* Always show the final "warning" event in the path.  */
+	  break;
+	}
+      idx--;
+    }
+}
+
+// FIXME: new implementation:
+/* First pass of diagnostic_manager::prune_path: apply verbosity level,
+   pruning unrelated state change events.
+
+   Iterate backwards through PATH, skipping state change events that aren't
+   VAR but update the pertinent VAR when state-copying occurs.
+
+   As well as deleting events, call record_critical_state on events in
+   which state critical to the pending_diagnostic is being handled, so
+   that the event's get_desc vfunc can potentially supply a more precise
+   description of the event to the user.
+   e.g. improving
+     "calling 'foo' from 'bar'"
+   to
+     "passing possibly-NULL pointer 'ptr' to 'foo' from 'bar' as param 1"
+   when the diagnostic relates to later dereferencing 'ptr'.  */
+
+void
+diagnostic_manager::prune_for_sm_diagnostic2 (checker_path *path,
+					      const state_machine *sm,
+					      const svalue2 *sval,
+					      state_machine::state_t state)
+  const
+{
+  int idx = path->num_events () - 1;
+  while (idx >= 0 && idx < (signed)path->num_events ())
+    {
+      checker_event *base_event = path->get_checker_event (idx);
+      if (get_logger ())
+	{
+	  if (sm)
+	    {
+	      if (sval)
+		{
+		  label_text sval_desc = sval->get_desc ();
+		  log ("considering event %i, with sval: %qs, state: %qs",
+		       idx, sval_desc.m_buffer, sm->get_state_name (state));
+		}
+	      else
+		log ("considering event %i, with global state: %qs",
+		     idx, sm->get_state_name (state));
+	    }
+	  else
+	    log ("considering event %i", idx);
+	}
+
+      switch (base_event->m_kind)
+	{
+	default:
+	  gcc_unreachable ();
+
+	case EK_DEBUG:
+	  if (m_verbosity < 4)
+	    {
+	      log ("filtering event %i: debug event", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
+
+	case EK_CUSTOM:
+	  /* Don't filter custom events.  */
+	  break;
+
+	case EK_STMT:
+	  {
+	    /* If this stmt is the origin of "sval", update sval.  */
+	    if (sval)
+	      {
+		statement_event *stmt_event = (statement_event *)base_event;
+		// TODO:
+#if 0
+		const svalue2 *new_sval
+		  = get_any_origin2 (stmt_event->m_stmt, sval,
+				     stmt_event->m_dst_state);
+		if (new_sval)
+		  {
+		    if (get_logger ())
+		      {
+			label_text sval_desc = sval->get_desc ();
+			label_text new_sval_desc = new_sval->get_desc ();
+			log ("event %i:"
+			     " switching var of interest from %qs to %qs",
+			     idx, sval_desc.m_buffer, new_sval_desc.m_buffer);
+		      }
+		    sval = new_sval;
+		  }
+#endif
+	      }
+	    if (m_verbosity < 4)
+	      {
+		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:
+	  /* New implementation should create EK_STATE_CHANGE2.  */
+	  gcc_unreachable ();
+
+	case EK_STATE_CHANGE2:
+	  {
+	    state_change2_event *state_change
+	      = (state_change2_event *)base_event;
+	    bool ctxt_had_errors = false;
+	    gcc_assert (state_change->m_dst_state.m_region_model2);
+
+	    if (state_change->m_sval == sval)
+	      {
+		if (state_change->m_origin)
+		  {
+		    if (get_logger ())
+		      {
+			label_text sval_desc = sval->get_desc ();
+			label_text origin_sval_desc
+			  = state_change->m_origin->get_desc ();
+			log ("event %i:"
+			     " switching var of interest from %qs to %qs",
+			     idx, sval_desc.m_buffer,
+			     origin_sval_desc.m_buffer);
+		      }
+		    sval = state_change->m_origin;
+		  }
+		log ("event %i: switching state of interest from %qs to %qs",
+		     idx, sm->get_state_name (state_change->m_to),
+		     sm->get_state_name (state_change->m_from));
+		state = state_change->m_from;
+	      }
+	    else if (m_verbosity < 4)
+	      {
+		if (get_logger ())
+		  {
+		    if (state_change->m_sval)
+		      {
+			label_text change_sval_desc
+			  = state_change->m_sval->get_desc ();
+			if (sval)
+			  {
+			    label_text sval_desc = sval->get_desc ();
+			    log ("filtering event %i:"
+				 " state change to %qs unrelated to %qs",
+				 idx, change_sval_desc.m_buffer,
+				 sval_desc.m_buffer);
+			  }
+			else
+			  log ("filtering event %i: state change to %qs",
+			       idx, change_sval_desc.m_buffer);
+		      }
+		    else
+		      log ("filtering event %i: global state change", idx);
+		  }
+		if (ctxt_had_errors)
+		  log ("context had errors");
+		path->delete_event (idx);
+	      }
+	  }
+	  break;
+
 	case EK_START_CFG_EDGE:
 	  {
 	    cfg_edge_event *event = (cfg_edge_event *)base_event;
 	    const cfg_superedge& cfg_superedge
 	      = event->get_cfg_superedge ();
 	    const supernode *dest = event->m_sedge->m_dest;
+	    // TODO:
+#if 0
 	    /* Do we have an SSA_NAME defined via a phi node in
 	       the dest CFG node?  */
 	    if (var && TREE_CODE (var) == SSA_NAME)
@@ -1495,6 +1807,7 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 		      update_for_unsuitable_sm_exprs (&var);
 		    }
 		}
+#endif
 
 	    /* TODO: is this edge significant to var?
 	       See if var can be in other states in the dest, but not
@@ -1520,6 +1833,8 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 
 	case EK_CALL_EDGE:
 	  {
+	    // TODO
+#if 0
 	    call_event *event = (call_event *)base_event;
 	    const callgraph_superedge& cg_superedge
 	      = event->get_callgraph_superedge ();
@@ -1537,6 +1852,7 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 		  event->record_critical_state (var, state);
 		update_for_unsuitable_sm_exprs (&var);
 	      }
+#endif
 	  }
 	  break;
 
@@ -1544,6 +1860,8 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 	  // TODO: potentially update var/state based on return value,
 	  // args etc
 	  {
+	    // TODO
+#if 0
 	    if (var)
 	      {
 		return_event *event = (return_event *)base_event;
@@ -1564,6 +1882,7 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 		    update_for_unsuitable_sm_exprs (&var);
 		  }
 	      }
+#endif
 	  }
 	  break;
 
diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
index c6f3c0c4ad1..d72bac55794 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -39,7 +39,8 @@ public:
 		    const exploded_node *enode,
 		    const supernode *snode, const gimple *stmt,
 		    stmt_finder *stmt_finder,
-		    tree var, state_machine::state_t state,
+		    tree var, const svalue2 *sval,
+		    state_machine::state_t state,
 		    pending_diagnostic *d);
   ~saved_diagnostic ();
 
@@ -73,6 +74,7 @@ public:
   const gimple *m_stmt;
   stmt_finder *m_stmt_finder;
   tree m_var;
+  const svalue2 *m_sval;
   state_machine::state_t m_state;
   pending_diagnostic *m_d;
   exploded_edge *m_trailing_eedge;
@@ -99,13 +101,17 @@ class path_builder;
 class diagnostic_manager : public log_user
 {
 public:
-  diagnostic_manager (logger *logger, int verbosity);
+  diagnostic_manager (logger *logger, engine2 *eng, int verbosity);
+
+  engine2 *get_engine2 () const { return m_eng; }
 
   void add_diagnostic (const state_machine *sm,
 		       const exploded_node *enode,
 		       const supernode *snode, const gimple *stmt,
 		       stmt_finder *finder,
-		       tree var, state_machine::state_t state,
+		       tree var,
+		       const svalue2 *sval,
+		       state_machine::state_t state,
 		       pending_diagnostic *d);
 
   void add_diagnostic (const exploded_node *enode,
@@ -152,16 +158,23 @@ private:
 
   void prune_path (checker_path *path,
 		   const state_machine *sm,
-		   tree var, state_machine::state_t state) const;
+		   tree var,
+		   const svalue2 *sval,
+		   state_machine::state_t state) const;
 
   void prune_for_sm_diagnostic (checker_path *path,
 				const state_machine *sm,
 				tree var,
 				state_machine::state_t state) const;
+  void prune_for_sm_diagnostic2 (checker_path *path,
+				 const state_machine *sm,
+				 const svalue2 *sval,
+				 state_machine::state_t state) const;
   void update_for_unsuitable_sm_exprs (tree *expr) const;
   void prune_interproc_events (checker_path *path) const;
   void finish_pruning (checker_path *path) const;
 
+  engine2 *m_eng;
   auto_delete_vec<saved_diagnostic> m_saved_diagnostics;
   const int m_verbosity;
 };
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 26f9f45d489..f9ed0216ef7 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -404,7 +404,7 @@ public:
       {
 	m_eg.get_diagnostic_manager ().add_diagnostic
 	  (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
-	   var, state, d);
+	   var, NULL, state, d);
       }
     else
       delete d;
@@ -566,9 +566,11 @@ public:
 
     if (state == current)
       {
+	const svalue2 *var_old_sval
+	  = m_old_state->m_region_model2->get_rvalue (var, &old_ctxt);
 	m_eg.get_diagnostic_manager ().add_diagnostic
 	  (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
-	   var, state, d);
+	   var, var_old_sval, state, d);
       }
     else
       delete d;
@@ -882,7 +884,7 @@ impl_region_model_context::on_state_leak (const state_machine &sm,
     m_eg->get_diagnostic_manager ().add_diagnostic
       (&sm, m_enode_for_diag, m_enode_for_diag->get_supernode (),
        m_stmt, &stmt_finder,
-       leaked_tree, state, pd);
+       leaked_tree, NULL, state, pd);
 }
 
 /* Implementation of region_model_context::on_inherited_svalue vfunc
@@ -1103,7 +1105,7 @@ impl_region_model2_context::on_state_leak (const state_machine &sm,
     m_eg->get_diagnostic_manager ().add_diagnostic
       (&sm, m_enode_for_diag, m_enode_for_diag->get_supernode (),
        m_stmt, &stmt_finder,
-       leaked_tree, state, pd);
+       leaked_tree, sval, state, pd);
 }
 
 /* Implementation of region_model_context::on_condition vfunc.
@@ -2578,7 +2580,7 @@ exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
   m_ext_state (ext_state),
   m_purge_map (purge_map),
   m_plan (plan),
-  m_diagnostic_manager (logger, verbosity),
+  m_diagnostic_manager (logger, ext_state.get_engine2 (), verbosity),
   m_global_stats (m_sg.num_nodes ()),
   m_functionless_stats (m_sg.num_nodes ()),
   m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h
index b1ff2688bcc..86eaab34d39 100644
--- a/gcc/analyzer/pending-diagnostic.h
+++ b/gcc/analyzer/pending-diagnostic.h
@@ -50,7 +50,7 @@ struct state_change : public event_desc
 		state_machine::state_t old_state,
 		state_machine::state_t new_state,
 		diagnostic_event_id_t event_id,
-		const state_change_event &event)
+		const base_state_change_event &event)
   : event_desc (colorize),
     m_expr (expr), m_origin (origin),
     m_old_state (old_state), m_new_state (new_state),
@@ -64,7 +64,7 @@ struct state_change : public event_desc
   state_machine::state_t m_old_state;
   state_machine::state_t m_new_state;
   diagnostic_event_id_t m_event_id;
-  const state_change_event &m_event;
+  const base_state_change_event &m_event;
 };
 
 /* For use by pending_diagnostic::describe_call_with_state.  */
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 0e7eaa0cc94..22072ac3c98 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -471,7 +471,7 @@ public:
   virtual bool on_state_change (const state_machine &sm,
 				state_machine::state_t src_sm_val,
 				state_machine::state_t dst_sm_val,
-				tree dst_rep,
+				const svalue2 *dst_sval,
 				const svalue2 *dst_origin_sval) = 0;
 };
 
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 274c5ed0362..d0fcb0d0101 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -191,6 +191,17 @@ svalue2::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* FIXME.  */
+
+label_text
+svalue2::get_desc (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  dump_to_pp (&pp, simple);
+  return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
 /* If this svalue2 is a constant_svalue2, return the underlying tree constant.
    Otherwise return NULL_TREE.  */
 
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 39a43252113..27eb83ad2cb 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -176,6 +176,7 @@ public:
 
   virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
   void dump (bool simple=true) const;
+  label_text get_desc (bool simple=true) const;
 
   virtual const region_svalue2 *
   dyn_cast_region_svalue2 () const { return NULL; }
diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
index 6cbb10c8ab9..03036d58280 100644
--- a/gcc/analyzer/sm-signal.cc
+++ b/gcc/analyzer/sm-signal.cc
@@ -138,8 +138,7 @@ public:
     if (change.is_global_p ()
 	&& change.m_new_state == m_sm.m_in_signal_handler)
       {
-	function *handler
-	  = change.m_event.m_dst_state.get_current_function ();
+	function *handler = change.m_event.get_dest_function ();
 	return change.formatted_print ("registering %qD as signal handler",
 				       handler->decl);
       }
-- 
2.26.2

