From 109e6bf8f35582e43f0ceb97b355a4b77554c637 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 25 Apr 2019 17:43:36 -0400
Subject: [PATCH 200/217] FIXME: impl-4: more on diagnostic_paths, taking
 material from impl-3

---
 gcc/analyzer/impl-4.cc | 799 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 796 insertions(+), 3 deletions(-)

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index f923253..a70824a 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -504,6 +504,537 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////
 
+/* FIXME.  */
+
+enum event_kind
+{
+  EK_DEBUG,
+  EK_STMT,
+  EK_FUNCTION_ENTRY,
+  EK_STATE_CHANGE,
+  EK_EDGE,
+  EK_WARNING
+};
+
+static const char *
+event_kind_to_string (enum event_kind ek)
+{
+  switch (ek)
+    {
+    default:
+      gcc_unreachable ();
+    case EK_DEBUG:
+      return "EK_DEBUG";
+    case EK_STMT:
+      return "EK_STMT";
+    case EK_FUNCTION_ENTRY:
+      return "EK_FUNCTION_ENTRY";
+    case EK_STATE_CHANGE:
+      return "EK_STATE_CHANGE";
+    case EK_EDGE:
+      return "EK_EDGE";
+    case EK_WARNING:
+      return "EK_WARNING";
+    }
+}
+
+/* FIXME.  */
+
+class checker4_event : public diagnostic_event
+{
+public:
+  checker4_event (enum event_kind kind,
+		  location_t loc, tree fndecl, int depth)
+    : m_kind (kind), m_loc (loc), m_fndecl (fndecl), m_depth (depth),
+      m_pending_diagnostic (NULL), m_emission_id ()
+  {
+  }
+
+  location_t get_location () const FINAL OVERRIDE { return m_loc; }
+  tree get_fndecl () const FINAL OVERRIDE { return m_fndecl; }
+  int get_stack_depth () const FINAL OVERRIDE { return m_depth; }
+
+  virtual checker4_event *clone () const = 0;
+
+  std::string to_str () const
+  {
+    std::string result ("\"");
+    label_text event_desc (get_desc ());
+    result += event_desc.m_buffer;
+    event_desc.maybe_free ();
+    result += "\" (depth ";
+    result += std::to_string (get_stack_depth ());
+    result += ", m_loc=";
+    result += std::to_string (get_location ());
+    result += ")";
+    return result;
+  }
+
+  void prepare_for_emission (pending_diagnostic *pd, event_id_t emission_id)
+  {
+    m_pending_diagnostic = pd;
+    m_emission_id = emission_id;
+
+    /* Call get_desc now, to ensure that any side-effects take place
+       before pending_diagnostic::emit is called.
+       In particular, state_change_event::get_desc can call
+       pending_diagnostic::describe_critical_state_change;
+       free_of_non_heap can use this to tweak the message.
+       (Would be neater to simply capture this within the state).  */
+    label_text desc = get_desc ();
+    desc.maybe_free ();
+  }
+
+  enum event_kind m_kind;
+  location_t m_loc;
+  tree m_fndecl;
+  int m_depth;
+  pending_diagnostic *m_pending_diagnostic;
+  event_id_t m_emission_id; // only set once all pruning has occurred
+};
+
+class debug_event : public checker4_event
+{
+public:
+  debug_event (location_t loc, tree fndecl, int depth,
+	      const char *desc)
+  : checker4_event (EK_DEBUG, loc, fndecl, depth),
+    m_desc (xstrdup (desc))
+  {
+  }
+  ~debug_event ()
+  {
+    free (m_desc);
+  }
+
+  label_text get_desc () const FINAL OVERRIDE
+  {
+    return label_text::borrow (m_desc);
+  }
+
+  checker4_event *clone () const FINAL OVERRIDE
+  {
+    return new debug_event (m_loc, m_fndecl, m_depth, m_desc);
+  }
+
+private:
+  char *m_desc;
+};
+
+class statement_event : public checker4_event
+{
+public:
+  statement_event (const gimple *stmt, tree fndecl, int depth)
+  : checker4_event (EK_STMT, gimple_location (stmt), fndecl, depth),
+    m_stmt (stmt)
+  {
+  }
+
+  label_text get_desc () const FINAL OVERRIDE
+  {
+    pretty_printer pp;
+    pp_string (&pp, "stmt: ");
+    // FIXME: const_cast!
+    pp_gimple_stmt_1 (&pp, const_cast <gimple *> (m_stmt) , 0, (dump_flags_t)0);
+    return label_text::take (xstrdup (pp_formatted_text (&pp)));
+  }
+
+  checker4_event *clone () const FINAL OVERRIDE
+  {
+    return new statement_event (m_stmt, m_fndecl, m_depth);
+  }
+
+  const gimple *m_stmt;
+};
+
+class function_entry_event : public checker4_event
+{
+public:
+  function_entry_event (location_t loc, tree fndecl, int depth)
+  : checker4_event (EK_FUNCTION_ENTRY, loc, fndecl, depth)
+  {
+  }
+
+  label_text get_desc () const FINAL OVERRIDE
+  {
+    return label_text::take (print ("entry to %qE", m_fndecl));
+  }
+
+  checker4_event *clone () const FINAL OVERRIDE
+  {
+    return new function_entry_event (m_loc, m_fndecl, m_depth);
+  }
+
+  // FIXME
+};
+
+class state_change_event : public checker4_event
+{
+public:
+  state_change_event (const supernode *node, const gimple *stmt,
+		      int stack_depth,
+		      const state_machine &sm,
+		      tree var,
+		      state_machine::state_t from,
+		      state_machine::state_t to,
+		      tree origin)
+  : checker4_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)
+  {
+  }
+
+  label_text get_desc () const FINAL OVERRIDE
+  {
+    if (m_origin)
+      return label_text::take
+	(print ("state of %qE: %qs -> %qs (origin: %qE)",
+		m_var,
+		m_sm.get_state_name (m_from),
+		m_sm.get_state_name (m_to),
+		m_origin));
+    else
+      {
+	/* If there is no origin, then presumably this
+	   state change came directly from a pattern match.
+	   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.  */
+	if (m_pending_diagnostic)
+	  {
+	    label_text custom_desc
+	      = m_pending_diagnostic->describe_critical_state_change
+	      (//diagnostic_context *ctxt,
+	       m_var, m_from, m_to, m_emission_id);
+	    if (custom_desc.m_buffer)
+	      {
+		if (flag_analyzer_verbose_state_changes)
+		  {
+		    /* Append debug version.  */
+		    label_text result = label_text::take
+		      (print ("%s (state of %qE: %qs -> %qs, origin: NULL)",
+			      custom_desc.m_buffer,
+			      m_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.  */
+	return label_text::take
+	  (print ("state of %qE: %qs -> %qs (origin: NULL)",
+		  m_var,
+		  m_sm.get_state_name (m_from),
+		  m_sm.get_state_name (m_to)));
+      }
+  }
+
+  checker4_event *clone () const FINAL OVERRIDE
+  {
+    return new state_change_event (m_node, m_stmt, m_depth,
+				   m_sm, m_var, m_from, m_to, m_origin);
+  }
+
+  const supernode *m_node;
+  const gimple *m_stmt;
+  const state_machine &m_sm;
+  tree m_var;
+  state_machine::state_t m_from;
+  state_machine::state_t m_to;
+  tree m_origin;
+};
+
+class edge_event : public checker4_event
+{
+public:
+  edge_event (const superedge *edge,
+	      location_t loc, tree fndecl, int depth)
+  : checker4_event (EK_EDGE,
+		   loc, fndecl, depth),
+    m_edge (edge)
+  {
+  }
+
+  label_text get_desc () const FINAL OVERRIDE
+  {
+    switch (m_edge->m_kind)
+      {
+      default:
+	gcc_unreachable ();
+      case SUPEREDGE_CFG_EDGE:
+	{
+	  char *edge_desc = m_edge->get_description ();
+	  if (strlen (edge_desc) > 0)
+	    {
+	      label_text result
+		= label_text::take (print ("taking %qs edge SN:%i -> SN:%i",
+					   edge_desc,
+					   m_edge->m_src->m_index,
+					   m_edge->m_dest->m_index));
+	      free (edge_desc);
+	      return result;
+	    }
+	  else
+	    {
+	      free (edge_desc);
+	      return label_text::take (print ("taking edge SN:%i -> SN:%i",
+					      m_edge->m_src->m_index,
+					      m_edge->m_dest->m_index));
+	    }
+	}
+      case SUPEREDGE_CALL:
+	{
+	  return label_text::take (print ("calling %qE from %qE",
+					  m_edge->m_dest->m_fun->decl,
+					  m_edge->m_src->m_fun->decl));
+	}
+
+      case SUPEREDGE_RETURN:
+	{
+	  return label_text::take (print ("returning to %qE from %qE",
+					  m_edge->m_dest->m_fun->decl,
+					  m_edge->m_src->m_fun->decl));
+	}
+
+      case SUPEREDGE_INTRAPROCEDURAL_CALL:
+	return label_text::take (// TODO: possibly a higher-level description?
+				 print ("taking edge SN:%i -> SN:%i",
+					m_edge->m_src->m_index,
+					m_edge->m_dest->m_index));
+      }
+  }
+
+  const cfg_superedge& get_cfg_superedge () const
+  {
+    gcc_assert (m_edge->m_kind == SUPEREDGE_CFG_EDGE);
+    return *m_edge->dyn_cast_cfg_superedge ();
+  }
+
+  const call_superedge& get_call_superedge () const
+  {
+    gcc_assert (m_edge->m_kind != SUPEREDGE_CFG_EDGE);
+    return *m_edge->dyn_cast_call_superedge ();
+  }
+
+  checker4_event *clone () const FINAL OVERRIDE
+  {
+    return new edge_event (m_edge, m_loc, m_fndecl, m_depth);
+  }
+
+  const superedge *m_edge;
+};
+
+/* FIXME: a repeat of the warning message at the end of the path.  */
+
+class warning_event : public checker4_event
+{
+public:
+  warning_event (location_t loc, tree fndecl, int depth,
+		 const char *msg)
+  : checker4_event (EK_WARNING, loc, fndecl, depth),
+    m_msg (xstrdup (msg))
+  {
+  }
+  ~warning_event () { free (m_msg); }
+
+  label_text get_desc () const FINAL OVERRIDE
+  {
+    return label_text::borrow (m_msg);
+  }
+
+  warning_event *clone () const FINAL OVERRIDE
+  {
+    return new warning_event (m_loc, m_fndecl, m_depth, m_msg);
+  }
+
+  char *m_msg;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+class checker4_path : public diagnostic_path
+{
+public:
+#if 0
+  struct link
+  {
+    link (const superedge *edge, const supernode *node)
+    : m_edge (edge), m_node (node)
+    {}
+
+    const superedge *m_edge;
+    const supernode *m_node;
+  };
+#endif
+
+  checker4_path ()
+  : diagnostic_path ()
+#if 0
+    m_links (),
+    m_active_calls (),
+    m_ps (),
+    m_terminator ()
+#endif
+  {}
+  checker4_path (const checker4_path &other);
+  checker4_path& operator= (const checker4_path &other);
+
+  std::string to_str () const;
+
+  void maybe_log (logger *logger, const char *desc) const
+  {
+    if (!logger)
+      return;
+    logger->log ("%s: %s", desc, to_str ().c_str ());
+    for (unsigned i = 0; i < m_events.length (); i++)
+      logger->log ("%s[%i]: %s",
+		   desc, i, m_events[i]->to_str ().c_str ());
+  }
+
+  unsigned num_events () const FINAL OVERRIDE
+  {
+    return m_events.length ();
+  }
+
+  const diagnostic_event & get_event (int idx) const FINAL OVERRIDE
+  {
+    return *m_events[idx];
+  }
+
+  void add_event (checker4_event *event)
+  {
+    m_events.safe_push (event);
+  }
+
+#if 0
+  void delete_event (int idx)
+  {
+    checker4_event *event = m_events[idx];
+    m_events.ordered_remove (idx);
+    delete event;
+  }
+
+  const supernode *get_end_node () const
+  {
+    gcc_assert (m_links.length () > 0);
+    return m_links[m_links.length () - 1].m_node;
+  }
+
+  /* Get the SUPEREDGE_RETURN corresponding to the most recent
+     unreturned SUPEREDGE_CALL.  Return NULL if there isn't one.  */
+  const superedge *get_next_return_edge () const
+  {
+    if (m_active_calls.length () == 0)
+      return NULL;
+    cgraph_edge *cedge = m_active_calls[m_active_calls.length () - 1];
+    return get_sg ().get_edge_for_return (cedge);
+  }
+
+  void add_link (const superedge *edge, const supernode *node);
+
+  int get_stack_depth () const { return m_ps.m_stack_depth; }
+
+  state_machine::state_t get_baseline_state (tree expr);
+  void set_state (tree expr,
+		  state_machine::state_t baseline_state,
+		  state_machine::state_t new_state);
+
+  equiv_class &get_ec (tree expr)
+  {
+    path_var pv (expr, get_stack_depth ());
+    equiv_class_id ec_id = m_ps.get_or_add_equiv_class (pv);
+    return ec_id.get_obj (m_ps);
+  }
+
+  const supergraph &get_sg () const;
+  const state_machine &get_sm () const;
+
+  int get_verbosity () const;
+#endif
+
+  void add_final_event (const supernode *node, const gimple *stmt,
+			tree var, state_machine::state_t state,
+			pending_diagnostic *pd)
+  {
+#if 0
+    char *msg = NULL;
+    label_text ev_desc = pd->describe_final_event (var, state);
+    if (ev_desc.m_buffer)
+      {
+	if (flag_analyzer_verbose_state_changes)
+	  {
+	    msg = print ("%s (%qE is in state %qs)", ev_desc.m_buffer,
+			 var, get_sm ().get_state_name (state));
+	    ev_desc.maybe_free ();
+	  }
+	else
+	  msg = ev_desc.take_or_copy ();
+      }
+    else
+      {
+	msg = print ("here (%qE is in state %qs)", var,
+		     get_sm ().get_state_name (state));
+	// FIXME: ideally would show the full message here, too?
+      }
+    checker4_event *end_of_path
+      = new warning_event (stmt->location,
+			   node->m_fun->decl, get_stack_depth (),
+			   msg);
+    add_event (end_of_path);
+#endif
+  }
+
+  void prepare_for_emission (pending_diagnostic *pd)
+  {
+    checker4_event *e;
+    int i;
+    FOR_EACH_VEC_ELT (m_events, i, e)
+      e->prepare_for_emission (pd, event_id_t (i));
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /* The events that have occurred along this path.  */
+  auto_delete_vec<checker4_event> m_events;
+
+#if 0
+  /* A trace of nodes and edges that the path has followed.  */
+  auto_vec <link> m_links;
+
+  /* The outstanding calls, so that we can find where we should
+     return to.  */
+  auto_vec <cgraph_edge *> m_active_calls;
+
+  /* The current state of memory.  */
+  path_state m_ps;
+
+  terminator m_terminator;
+
+  /* FIXME: map from EC to state (or subclass path_state?)
+     No, store them in EC's m_extra_state.  */
+
+  /* TODO: add additional things, like edge counts, etc
+     (as an optimization; these could be derived by walking
+     the events).  */
+#endif
+};
+
+////////////////////////////////////////////////////////////////////////////
+
 void
 program_point::print (pretty_printer *pp) const
 {
@@ -647,6 +1178,7 @@ public:
 	auto_vec<const exploded_edge *> exploded_path;
 	sp.get_shortest_path (m_node, &exploded_path);
 	pretty_printer *pp = global_dc->printer->clone ();
+#if 0
 	simple_diagnostic_path emission_path (pp);
 	for (int i = 0; i < exploded_path.length (); i++)
 	  {
@@ -654,15 +1186,77 @@ public:
 	      log ("EN %i -> EN %i",
 		   exploded_path[i]->m_src->m_index,
 		   exploded_path[i]->m_dest->m_index);
-	    exploded_node *node = exploded_path[i]->m_dest;
-	    if (node->m_ps.m_point.m_kind == PK_BEFORE_STMT)
+	    exploded_node *dst_node = exploded_path[i]->m_dest;
+	    if (dst_node->m_ps.m_point.m_kind == PK_BEFORE_STMT)
 	      {
-		emission_path.add_event (node->m_ps.m_point.get_stmt ()->location,
+		emission_path.add_event (dst->m_ps.m_point.get_stmt ()->location,
 					 NULL_TREE, /* FIXME */
 					 0, /* FIXME */
 					 "FIXME");
 	      }
 	  }
+#endif
+
+	checker4_path emission_path;
+	// TODO: build path
+	for (int i = 0; i < exploded_path.length (); i++)
+	  {
+	    const exploded_edge *eedge = exploded_path[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->m_ps.m_point;
+	    const exploded_node *dst_node = eedge->m_dest;
+	    const program_point &dst_point = dst_node->m_ps.m_point;
+	    switch (dst_point.m_kind)
+	      {
+	      default:
+		break;
+	      case PK_BEFORE_SUPERNODE:
+		if (src_point.m_kind == PK_AFTER_SUPERNODE)
+		  {
+		    if (eedge->m_sedge)
+		      {
+			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,
+					   0 /* FIXME: depth */ ));
+			/* TODO: state changes due to edges.  */
+		      }
+		  }
+		// 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,
+			0 /* FIXME: depth */ ));
+		  }
+		break;
+	      case PK_BEFORE_STMT:
+		emission_path.add_event
+		  (new statement_event (dst_point.get_stmt (),
+					dst_point.m_supernode->m_fun->decl,
+					0 /* FIXME: depth */ ));
+		break;
+	      case PK_AFTER_STMT:
+		/* TODO: state changes due to stmts.  */
+		break;
+	      }
+	  }
+	/* TODO: state changes due to stmts.  */
+
+	//m_engine->prune_path (&emission_path, var, state);
+	emission_path.add_final_event (node, stmt, var, state, d);
+	emission_path.prepare_for_emission (d);
 
 	gcc_rich_location rich_loc (stmt->location);
 	rich_loc.set_path (&emission_path);
@@ -1255,6 +1849,205 @@ shortest_paths::get_shortest_path (const exploded_node *to,
 
 ////////////////////////////////////////////////////////////////////////////
 
+////////////////////////////////////////////////////////////////////////////
+
+#if 0
+checker4_path::checker4_path (const checker4_path &other)
+: m_engine (other.m_engine),
+  m_links (),
+  m_ps (other.m_ps),
+  m_terminator (other.m_terminator)
+{
+  m_engine->m_num_path_copies++;
+  m_engine->m_num_event_copies += other.m_events.length ();
+
+  unsigned i;
+  {
+    link *l;
+    FOR_EACH_VEC_ELT (other.m_links, i, l)
+      m_links.safe_push (*l);
+  }
+  {
+    cgraph_edge *edge;
+    FOR_EACH_VEC_ELT (other.m_active_calls, i, edge)
+      m_active_calls.safe_push (edge);
+  }
+  {
+    checker4_event *e;
+    FOR_EACH_VEC_ELT (other.m_events, i, e)
+      m_events.safe_push (e->clone ());
+  }
+  // FIXME: quick push for these?
+}
+
+checker4_path&
+checker4_path::operator= (const checker4_path &other)
+{
+  m_engine = other.m_engine;
+  m_ps = other.m_ps;
+  m_terminator = other.m_terminator;
+
+  m_engine->m_num_path_copies++;
+  m_engine->m_num_event_copies += other.m_events.length ();
+
+  unsigned i;
+  {
+    link *l;
+    FOR_EACH_VEC_ELT (other.m_links, i, l)
+      m_links.safe_push (*l);
+  }
+  {
+    cgraph_edge *edge;
+    FOR_EACH_VEC_ELT (other.m_active_calls, i, edge)
+      m_active_calls.safe_push (edge);
+  }
+  {
+    checker4_event *e;
+    FOR_EACH_VEC_ELT (other.m_events, i, e)
+      m_events.safe_push (e->clone ());
+  }
+  return *this;
+}
+
+/* FIXME.  */
+std::string
+checker4_path::to_str () const
+{
+  std::string result ("[");
+
+  checker4_event *e;
+  int i;
+  FOR_EACH_VEC_ELT (m_events, i, e)
+    {
+      if (i > 0)
+	result += ", ";
+      label_text event_desc (e->get_desc ());
+      result += std::string ("\"") + event_desc.m_buffer + std::string ("\"");
+      event_desc.maybe_free ();
+    }
+  result += "]";
+  {
+    result += ", m_links=[";
+    link *link;
+    FOR_EACH_VEC_ELT (m_links, i, link)
+      {
+	if (i > 0)
+	  result += ", ";
+	result += std::to_string (link->m_node->m_index);
+      }
+    result += "]";
+  }
+  {
+    result += ", m_active_calls=[";
+    cgraph_edge *edge;
+    FOR_EACH_VEC_ELT (m_active_calls, i, edge)
+      {
+	if (i > 0)
+	  result += ", ";
+	result += m_engine->m_sg.get_edge_for_call (edge)->to_str ();
+      }
+    result += "]";
+  }
+  result += ", m_ps=" + m_ps.to_str ();
+  result += ", m_terminator=" + m_terminator.to_str ();
+  return result;
+}
+
+/* Record that we've reached NODE, via EDGE.  EDGE may be NULL,
+   to cover entrypoints to the analysis.
+   Update m_active_calls to reflect call and return edges.
+   Update m_terminator.  */
+
+void
+checker4_path::add_link (const superedge *edge, const supernode *node)
+{
+  m_links.safe_push (link (edge, node));
+  if (edge)
+    {
+      switch (edge->m_kind)
+	{
+	default:
+	  break;
+	case SUPEREDGE_CALL:
+	  {
+	    cgraph_edge *cedge = edge->get_callgraph_edge ();
+	    gcc_assert (cedge);
+	    m_active_calls.safe_push (cedge);
+	  }
+	  break;
+	case SUPEREDGE_RETURN:
+	  {
+	    cgraph_edge *top_cedge = m_active_calls.pop ();
+	    cgraph_edge *edge_cedge = edge->get_callgraph_edge ();
+	    gcc_assert (edge_cedge == top_cedge);
+	  }
+	  break;
+	}
+      m_terminator.add_edge (edge);
+    }
+  if (get_verbosity () > 1)
+    {
+      add_event (new debug_event (node->get_start_location (),
+				  node->m_fun->decl,
+				  get_stack_depth (),
+				  print ("SN: %i", node->m_index)));
+    }
+  if (get_verbosity () > 0 && node->entry_p ())
+    {
+      add_event
+	(new function_entry_event (node->get_start_location (),
+				   node->m_fun->decl,
+				   get_stack_depth ()));
+    }
+}
+
+state_machine::state_t
+checker4_path::get_baseline_state (tree expr)
+{
+  equiv_class &ec = get_ec (expr);
+  ec3_state *extra_state = (ec3_state *)ec.m_extra_state;
+  if (extra_state)
+    return extra_state->m_baseline_state;
+  else
+    return 0;  // FIXME "start"
+}
+
+void
+checker4_path::set_state (tree expr,
+			  state_machine::state_t baseline_state,
+			  state_machine::state_t new_state)
+{
+  equiv_class &ec = get_ec (expr);
+  delete ec.m_extra_state;
+  ec.m_extra_state = new ec3_state (get_sm (), baseline_state, new_state);
+  if (m_engine->get_logger ())
+    m_engine->log ("%qE state: %qs -> %qs",
+		   expr,
+		   get_sm ().get_state_name (baseline_state),
+		   get_sm ().get_state_name (new_state));
+}
+
+const supergraph &
+checker4_path::get_sg () const
+{
+  return m_engine->m_sg;
+}
+
+const state_machine &
+checker4_path::get_sm () const
+{
+  return m_engine->m_sm;
+}
+
+int
+checker4_path::get_verbosity () const
+{
+  return m_engine->m_verbosity;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+
 #if 0
 /* FIXME.  */
 
-- 
1.8.5.3

